diff --git a/wine-tkg-git/customization.cfg b/wine-tkg-git/customization.cfg index 95ee85790..ab5b04622 100644 --- a/wine-tkg-git/customization.cfg +++ b/wine-tkg-git/customization.cfg @@ -62,6 +62,12 @@ _staging_version="" # !! For plain Wine required disabling esync and fsync patches applying !! _use_fastsync="false" +# NTsync5 - https://repo.or.cz/wine/zf.git/shortlog/refs/heads/ntsync5 +# !! For building and using requires ntsync module and headers (see the three packages https://aur.archlinux.org/pkgbase/ntsync) !! +# !! Not compatible with _protonify, _use_staging, nor any of _use_esync, _use_fsync or _use_fastsync options at this time !! +# !! Not compatible with Valve trees !! +_use_ntsync="false" + # esync - Enable with WINEESYNC=1 envvar - Set to true to enable esync support on plain wine or wine-staging <4.6 (it got merged in wine-staging 4.6). The option is ignored on wine-staging 4.6+ # You may need to raise your fd limits -> https://github.com/zfigura/wine/blob/esync/README.esync _use_esync="true" 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 index 5f89f81b7..225cc7385 100644 --- 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 @@ -226,16 +226,18 @@ 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 +@@ -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" )) @@ -264,11 +266,13 @@ index e0e5006f498..f5e8a211ee6 100644 - while (*next); - - putenv( env ); +- ld_preload = NULL; - } - +- if (ld_preload) setenv( "WINE_LD_PRELOAD", ld_preload, 1 ); + signal( SIGPIPE, SIG_DFL ); - - sprintf( socket_env, "WINESERVERSOCKET=%u", socketfd ); + @@ -2140,22 +2109,8 @@ void *steamclient_handle_fault( LPCVOID addr, DWORD err ) return NULL; } diff --git a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/hotfixes b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/hotfixes index 0aadb5330..bb015251e 100644 --- a/wine-tkg-git/wine-tkg-patches/hotfixes/valve/hotfixes +++ b/wine-tkg-git/wine-tkg-patches/hotfixes/valve/hotfixes @@ -115,11 +115,11 @@ if [ "$_use_staging" = "true" ]; then -W user32-Mouse_Message_Hwnd \ -W wined3d-SWVP-shaders \ -W wined3d-Indexed_Vertex_Blending \ - -W winepulse-PulseAudio_Support) + -W winepulse-PulseAudio_Support \ + -W d3dx9_36-D3DXStubs \ + -W shell32-registry-lookup-app) if [[ "$_LOCAL_PRESET" = valve-ex* ]]; then - _staging_args+=(-W shell32-registry-lookup-app \ - -W d3dx9_36-D3DXStubs \ - -W ntdll-ext4-case-folder) + _staging_args+=(-W ntdll-ext4-case-folder) fi else _staging_args+=(-W winex11-_NET_ACTIVE_WINDOW \ 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 1ee809488..cc37b3a4b 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,10 @@ # 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 c0042af5cc2f6d03e3875f4cd4a7c97315dd0ab2 HEAD; then + if git merge-base --is-ancestor eb5993a7c6fbc1cd9deac0dceabc8f1c76e14ba8 HEAD; then _patchname='childwindow-proton.patch' && _patchmsg="Applied child window for vk patch" && nonuser_patcher + elif git merge-base --is-ancestor c0042af5cc2f6d03e3875f4cd4a7c97315dd0ab2 HEAD; then + _patchname='childwindow-proton-eb5993a.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 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 index 5f0157d5f..0da85f57e 100644 --- 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 @@ -336,7 +336,7 @@ index 7ceaeb7c2df..104d922c156 100644 if (ctx) sync_context( ctx ); - escape.gl_drawable = gl->pixmap; + escape.drawable = gl->pixmap; - if (pglXCopySubBufferMESA) { + if (ctx && 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 ) @@ -347,8 +347,8 @@ index 7ceaeb7c2df..104d922c156 100644 + if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; /* fall through */ default: -- if (escape.gl_drawable && pglXSwapBuffersMscOML) -+ if (escape.drawable && pglXSwapBuffersMscOML) +- if (ctx && escape.gl_drawable && pglXSwapBuffersMscOML) ++ if (ctx && escape.drawable && pglXSwapBuffersMscOML) { pglFlush(); target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); @@ -356,15 +356,15 @@ index 7ceaeb7c2df..104d922c156 100644 break; } -- if (escape.gl_drawable && pglXWaitForSbcOML) -+ if (escape.drawable && pglXWaitForSbcOML) +- if (ctx && escape.gl_drawable && pglXWaitForSbcOML) ++ if (ctx && 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 ); + NtGdiExtEscape( ctx ? ctx->hdc : 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 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 471ca7710..45e78ca30 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 @@ -336,7 +336,7 @@ index 7ceaeb7c2df..104d922c156 100644 if (ctx) sync_context( ctx ); - escape.gl_drawable = gl->pixmap; + escape.drawable = gl->pixmap; - if (pglXCopySubBufferMESA) { + if (ctx && 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 ) @@ -347,8 +347,8 @@ index 7ceaeb7c2df..104d922c156 100644 + if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window; /* fall through */ default: -- if (escape.gl_drawable && pglXSwapBuffersMscOML) -+ if (escape.drawable && pglXSwapBuffersMscOML) +- if (ctx && escape.gl_drawable && pglXSwapBuffersMscOML) ++ if (ctx && escape.drawable && pglXSwapBuffersMscOML) { pglFlush(); target_sbc = pglXSwapBuffersMscOML( gdi_display, gl->drawable, 0, 0, 0 ); @@ -356,15 +356,15 @@ index 7ceaeb7c2df..104d922c156 100644 break; } -- if (escape.gl_drawable && pglXWaitForSbcOML) -+ if (escape.drawable && pglXWaitForSbcOML) +- if (ctx && escape.gl_drawable && pglXWaitForSbcOML) ++ if (ctx && 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 ); + NtGdiExtEscape( ctx ? ctx->hdc : 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 diff --git a/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-eb5993a.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-eb5993a.patch new file mode 100644 index 000000000..471ca7710 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-eb5993a.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 = { + # 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,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 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/legacy/childwindow-proton-mainline-eb5993a.patch b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-mainline-eb5993a.patch new file mode 100644 index 000000000..5f0157d5f --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/childwindow/legacy/childwindow-proton-mainline-eb5993a.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/fastsync/fastsync b/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync index 9eca43171..af15da2a4 100644 --- a/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/fastsync @@ -71,4 +71,33 @@ _patchname='fastsync-clock_monotonic-fixup.patch' && _patchmsg="Applied fastsync fix due clock_monotonic" && nonuser_patcher fi + elif [ "$_use_ntsync" = "true" ]; then + + # check Linux headers + ntsync_header_check=$(echo '#include ' | gcc -H -E -fsyntax-only - 2>&1 | head -n1 | cut -d' ' -f2-) + if [[ ! -s $ntsync_header_check ]]; then + error $ntsync_header_check + error "NTsync header was not found. Build is forcibly prevented." + error 'Please use linux-tkg build with _ntsync="true" or https://aur.archlinux.org/packages/ntsync-dkms/' + exit 1 + fi + + if [ "$_use_staging" = "true" ]; then + error "NTsync for Staging trees isn't available at this point in time. Build is forcibly prevented. Please set _use_staging to false, or fallback to a different synchronization patchset" + exit 1 + fi + + if [ "$_protonify" = "true" ]; then + error "NTsync isn't compatible with enabled _protonify at this point in time. Build is forcibly prevented. Please disable it in your .cfg to use NTsync" + exit 1 + fi + + if [ "$_use_esync" = "false" ] && [ "$_use_fsync" = "false" ] && [ "$_use_fastsync" = "false" ]; then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor 59485f00c917061c097c1805d7fa7f61c380c749 HEAD ); then + _patchname='ntsync5.patch' && _patchmsg="Using ntsync patchset" && nonuser_patcher + fi + else + error "NTsync for Plain trees conflicts with _use_esync, _use_fsync and _use_fastsync. Build is forcibly prevented. Please disable them in your .cfg to use NTsync" + exit 1 + fi fi diff --git a/wine-tkg-git/wine-tkg-patches/misc/fastsync/ntsync5.patch b/wine-tkg-git/wine-tkg-patches/misc/fastsync/ntsync5.patch new file mode 100644 index 000000000..0c5836a1b --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/misc/fastsync/ntsync5.patch @@ -0,0 +1,4300 @@ +diff --git a/configure b/configure +index ae5def6a6b1..6848ae65ff9 100755 +--- a/configure ++++ b/configure +@@ -8004,6 +8004,12 @@ if test "x$ac_cv_header_linux_major_h" = xyes + then : + printf "%s\n" "#define HAVE_LINUX_MAJOR_H 1" >>confdefs.h + ++fi ++ac_fn_c_check_header_compile "$LINENO" "linux/ntsync.h" "ac_cv_header_linux_ntsync_h" "$ac_includes_default" ++if test "x$ac_cv_header_linux_ntsync_h" = xyes ++then : ++ printf "%s\n" "#define HAVE_LINUX_NTSYNC_H 1" >>confdefs.h ++ + fi + ac_fn_c_check_header_compile "$LINENO" "linux/param.h" "ac_cv_header_linux_param_h" "$ac_includes_default" + if test "x$ac_cv_header_linux_param_h" = xyes +diff --git a/configure.ac b/configure.ac +index 475743bc121..f4155253181 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -425,6 +425,7 @@ AC_CHECK_HEADERS(\ + linux/input.h \ + linux/ioctl.h \ + linux/major.h \ ++ linux/ntsync.h \ + linux/param.h \ + linux/serial.h \ + linux/types.h \ +diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c +index 56a9d6e4859..48e4f8d7b4d 100644 +--- a/dlls/kernel32/tests/sync.c ++++ b/dlls/kernel32/tests/sync.c +@@ -2841,6 +2841,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; +@@ -2911,5 +2989,6 @@ START_TEST(sync) + test_srwlock_example(); + test_alertable_wait(); + test_apc_deadlock(); ++ test_zigzag_event(); + test_crit_section(); + } +diff --git a/dlls/ntdll/unix/file.c b/dlls/ntdll/unix/file.c +index ee68e4dee9b..9f2707f9eeb 100644 +--- a/dlls/ntdll/unix/file.c ++++ b/dlls/ntdll/unix/file.c +@@ -6344,7 +6344,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 71eab55920d..8e4407e7a6d 100644 +--- a/dlls/ntdll/unix/process.c ++++ b/dlls/ntdll/unix/process.c +@@ -916,7 +916,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 69e6567c911..c90f8ab4019 100644 +--- a/dlls/ntdll/unix/server.c ++++ b/dlls/ntdll/unix/server.c +@@ -103,7 +103,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 ) +@@ -785,6 +785,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.@) + */ +@@ -846,7 +861,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 ) + { +@@ -1774,12 +1789,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 ) + { +@@ -1845,12 +1865,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 bfbcaf4a851..f650d072cd4 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 @@ + #ifdef HAVE_KQUEUE + # include + #endif ++#ifdef HAVE_LINUX_NTSYNC_H ++# include ++#endif + + #include "ntstatus.h" + #define WIN32_NO_STATUS +@@ -71,9 +77,11 @@ 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) ); + } + ++ + /* return a monotonic time counter, in Win32 ticks */ + static inline ULONGLONG monotonic_counter(void) + { +@@ -258,6 +266,908 @@ static unsigned int validate_open_object_attributes( const OBJECT_ATTRIBUTES *at + } + + ++#ifdef HAVE_LINUX_NTSYNC_H ++ ++static int get_linux_sync_device(void) ++{ ++ static LONG 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 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* ++ * 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; ++ int fd; ++ 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; ++ ++ /* 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 fd, ++ 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->fd = fd; ++ 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; ++ 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; ++ obj_handle_t fast_sync_handle; ++ enum fast_sync_type type; ++ unsigned int access; ++ int fd, needs_close; ++ NTSTATUS ret; ++ ++ /* 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 ))) ++ { ++ fast_sync_handle = reply->handle; ++ access = reply->access; ++ type = reply->type; ++ } ++ } ++ SERVER_END_REQ; ++ ++ if (ret) return ret; ++ ++ if ((ret = server_get_unix_fd( wine_server_ptr_handle( fast_sync_handle ), ++ 0, &fd, &needs_close, NULL, NULL ))) ++ return ret; ++ ++ cache = cache_fast_sync_obj( handle, fast_sync_handle, fd, type, access ); ++ if (!cache) ++ { ++ cache = stack_cache; ++ cache->handle = fast_sync_handle; ++ cache->fd = fd; ++ 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 ((cache->access & desired_access) != desired_access) ++ { ++ release_fast_sync_obj( cache ); ++ return STATUS_ACCESS_DENIED; ++ } ++ ++ 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 ); ++ } ++} ++ ++ ++static NTSTATUS linux_release_semaphore_obj( int obj, ULONG count, ULONG *prev_count ) ++{ ++ NTSTATUS ret; ++ ++ ret = ioctl( obj, NTSYNC_IOC_SEM_POST, &count ); ++ if (ret < 0) ++ { ++ if (errno == EOVERFLOW) ++ return STATUS_SEMAPHORE_LIMIT_EXCEEDED; ++ else ++ return errno_to_status( errno ); ++ } ++ if (prev_count) *prev_count = 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; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_semaphore_obj( cache->fd, count, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_semaphore_obj( int obj, SEMAPHORE_BASIC_INFORMATION *info ) ++{ ++ struct ntsync_sem_args args = {0}; ++ NTSTATUS ret; ++ ++ ret = ioctl( obj, NTSYNC_IOC_SEM_READ, &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; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_SEMAPHORE, ++ SEMAPHORE_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_semaphore_obj( cache->fd, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_set_event_obj( int obj, LONG *prev_state ) ++{ ++ NTSTATUS ret; ++ __u32 prev; ++ ++ ret = ioctl( obj, NTSYNC_IOC_EVENT_SET, &prev ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = prev; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_set_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_set_event_obj( cache->fd, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_reset_event_obj( int obj, LONG *prev_state ) ++{ ++ NTSTATUS ret; ++ __u32 prev; ++ ++ ret = ioctl( obj, NTSYNC_IOC_EVENT_RESET, &prev ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = prev; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_reset_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_reset_event_obj( cache->fd, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_pulse_event_obj( int obj, LONG *prev_state ) ++{ ++ NTSTATUS ret; ++ __u32 prev; ++ ++ ret = ioctl( obj, NTSYNC_IOC_EVENT_PULSE, &prev ); ++ if (ret < 0) ++ return errno_to_status( errno ); ++ if (prev_state) *prev_state = prev; ++ return STATUS_SUCCESS; ++} ++ ++ ++static NTSTATUS fast_pulse_event( HANDLE handle, LONG *prev_state ) ++{ ++ struct fast_sync_cache_entry stack_cache, *cache; ++ NTSTATUS ret; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_MODIFY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_pulse_event_obj( cache->fd, prev_state ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_event_obj( int obj, enum fast_sync_type type, EVENT_BASIC_INFORMATION *info ) ++{ ++ struct ntsync_event_args args = {0}; ++ NTSTATUS ret; ++ ++ ret = ioctl( obj, NTSYNC_IOC_EVENT_READ, &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; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_AUTO_EVENT, ++ EVENT_QUERY_STATE, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_event_obj( cache->fd, cache->type, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_release_mutex_obj( int obj, LONG *prev_count ) ++{ ++ struct ntsync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ args.owner = GetCurrentThreadId(); ++ ret = ioctl( obj, NTSYNC_IOC_MUTEX_UNLOCK, &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; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, 0, &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_release_mutex_obj( cache->fd, prev_count ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++ ++static NTSTATUS linux_query_mutex_obj( int obj, MUTANT_BASIC_INFORMATION *info ) ++{ ++ struct ntsync_mutex_args args = {0}; ++ NTSTATUS ret; ++ ++ ret = ioctl( obj, NTSYNC_IOC_MUTEX_READ, &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; ++ ++ if ((ret = get_fast_sync_obj( handle, FAST_SYNC_MUTEX, MUTANT_QUERY_STATE, ++ &stack_cache, &cache ))) ++ return ret; ++ ++ ret = linux_query_mutex_obj( cache->fd, info ); ++ ++ release_fast_sync_obj( cache ); ++ return ret; ++} ++ ++static __u64 fast_get_timeout( const LARGE_INTEGER *timeout ) ++{ ++ struct timespec now; ++ timeout_t relative; ++ ++ if (!timeout || timeout->QuadPart == TIMEOUT_INFINITE) ++ return ~(__u64)0; ++ ++ 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; ++ } ++ ++ return (now.tv_sec * 1000000000) + now.tv_nsec + (relative * 100); ++} ++ ++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 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; ++ unsigned int 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->fd; ++ release_fast_sync_obj( cache ); ++ NtClose( alert_handle ); ++ } ++ ++ return data->fast_alert_obj; ++} ++ ++static NTSTATUS linux_wait_objs( int device, const DWORD count, const int *objs, ++ BOOLEAN wait_any, BOOLEAN alertable, const LARGE_INTEGER *timeout ) ++{ ++ struct ntsync_wait_args args = {0}; ++ unsigned long request; ++ int ret; ++ ++ args.timeout = fast_get_timeout( 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 = NTSYNC_IOC_WAIT_ANY; ++ else ++ request = NTSYNC_IOC_WAIT_ALL; ++ ++ 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]; ++ int device, objs[64]; ++ HANDLE queue = NULL; ++ NTSTATUS ret; ++ DWORD i, j; ++ ++ 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]->fd; ++ } ++ ++ /* 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 ); ++ ++ 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( signal_cache->fd, 1, NULL ); ++ break; ++ ++ case FAST_SYNC_AUTO_EVENT: ++ case FAST_SYNC_MANUAL_EVENT: ++ ret = linux_set_event_obj( signal_cache->fd, NULL ); ++ break; ++ ++ case FAST_SYNC_MUTEX: ++ ret = linux_release_mutex_obj( signal_cache->fd, NULL ); ++ break; ++ ++ default: ++ assert( 0 ); ++ break; ++ } ++ ++ if (!ret) ++ { ++ if (queue) select_queue( queue ); ++ ret = linux_wait_objs( device, 1, &wait_cache->fd, TRUE, alertable, timeout ); ++ if (queue) unselect_queue( queue, !ret ); ++ } ++ ++ release_fast_sync_obj( signal_cache ); ++ release_fast_sync_obj( wait_cache ); ++ return ret; ++} ++ ++#else ++ ++void close_fast_sync_obj( HANDLE handle ) ++{ ++} ++ ++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.@) + */ +@@ -268,6 +1178,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", (int)access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", (int)initial, (int)max ); ++ + *handle = 0; + if (max <= 0 || initial < 0 || initial > max) return STATUS_INVALID_PARAMETER; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; +@@ -295,6 +1208,8 @@ NTSTATUS WINAPI NtOpenSemaphore( HANDLE *handle, ACCESS_MASK access, const OBJEC + { + unsigned int ret; + ++ TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -332,6 +1247,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 ); +@@ -354,6 +1275,11 @@ NTSTATUS WINAPI NtReleaseSemaphore( HANDLE handle, ULONG count, ULONG *previous + { + unsigned int ret; + ++ TRACE( "handle %p, count %u, prev_count %p\n", handle, (int)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 ); +@@ -378,6 +1304,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", (int)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; +@@ -405,6 +1334,8 @@ NTSTATUS WINAPI NtOpenEvent( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + unsigned int ret; + ++ TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -430,6 +1361,11 @@ NTSTATUS WINAPI NtSetEvent( HANDLE handle, LONG *prev_state ) + { + unsigned int ret; + ++ 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 ); +@@ -449,6 +1385,11 @@ NTSTATUS WINAPI NtResetEvent( HANDLE handle, LONG *prev_state ) + { + unsigned int ret; + ++ 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 ); +@@ -478,6 +1419,11 @@ NTSTATUS WINAPI NtPulseEvent( HANDLE handle, LONG *prev_state ) + { + unsigned int ret; + ++ 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 ); +@@ -509,6 +1455,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 ); +@@ -534,6 +1486,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", (int)access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", owned ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -559,6 +1514,8 @@ NTSTATUS WINAPI NtOpenMutant( HANDLE *handle, ACCESS_MASK access, const OBJECT_A + { + unsigned int ret; + ++ TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -584,6 +1541,11 @@ NTSTATUS WINAPI NtReleaseMutant( HANDLE handle, LONG *prev_count ) + { + unsigned int ret; + ++ 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 ); +@@ -614,6 +1576,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 ); +@@ -1321,6 +2289,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", (int)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; +@@ -1348,6 +2319,8 @@ NTSTATUS WINAPI NtOpenTimer( HANDLE *handle, ACCESS_MASK access, const OBJECT_AT + { + unsigned int ret; + ++ TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1401,6 +2374,8 @@ NTSTATUS WINAPI NtCancelTimer( HANDLE handle, BOOLEAN *state ) + { + unsigned int ret; + ++ TRACE( "handle %p, state %p\n", handle, state ); ++ + SERVER_START_REQ( cancel_timer ) + { + req->handle = wine_server_obj_handle( handle ); +@@ -1469,13 +2444,29 @@ NTSTATUS WINAPI NtWaitForMultipleObjects( DWORD count, const HANDLE *handles, BO + { + select_op_t select_op; + UINT i, flags = SELECT_INTERRUPTIBLE; ++ unsigned int 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 ((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] ); +- 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; + } + + +@@ -1496,9 +2487,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 ); +@@ -1721,6 +2718,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", (int)access, ++ attr ? debugstr_us(attr->ObjectName) : "(null)", (int)flags ); ++ + *handle = 0; + if ((ret = alloc_object_attributes( attr, &objattr, &len ))) return ret; + +@@ -1745,6 +2745,8 @@ NTSTATUS WINAPI NtOpenKeyedEvent( HANDLE *handle, ACCESS_MASK access, const OBJE + { + unsigned int ret; + ++ TRACE( "access %#x, name %s\n", (int)access, attr ? debugstr_us(attr->ObjectName) : "(null)" ); ++ + *handle = 0; + if ((ret = validate_open_object_attributes( attr ))) return ret; + +@@ -1771,6 +2773,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; +@@ -1790,6 +2794,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; +diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c +index 9e84ec3cc96..d0cb1db090e 100644 +--- a/dlls/ntdll/unix/thread.c ++++ b/dlls/ntdll/unix/thread.c +@@ -1755,7 +1755,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 07f2724eac7..e8dd9bbb035 100644 +--- a/dlls/ntdll/unix/unix_private.h ++++ b/dlls/ntdll/unix/unix_private.h +@@ -101,6 +101,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 */ ++ int fast_alert_obj; /* fd for the fast alert event */ + }; + + C_ASSERT( sizeof(struct ntdll_thread_data) <= sizeof(((TEB *)0)->GdiTebBatch) ); +@@ -192,6 +193,8 @@ extern NTSTATUS load_main_exe( const WCHAR *name, const char *unix_name, const W + extern NTSTATUS load_start_exe( WCHAR **image, void **module ); + extern void start_server( BOOL debug ); + ++extern pthread_mutex_t fd_cache_mutex; ++ + 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 ); +@@ -199,6 +202,7 @@ 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 ); + extern unsigned int server_wait( const select_op_t *select_op, data_size_t size, UINT flags, + const LARGE_INTEGER *timeout ); ++extern unsigned int server_wait_for_object( HANDLE handle, BOOL alertable, const LARGE_INTEGER *timeout ); + extern unsigned int server_queue_process_apc( HANDLE process, const apc_call_t *call, + apc_result_t *result ); + extern int server_get_unix_fd( HANDLE handle, unsigned int wanted_access, int *unix_fd, +@@ -335,6 +339,8 @@ extern NTSTATUS wow64_wine_spawnvp( void *args ); + + extern void dbg_init(void); + ++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 ); + extern NTSTATUS call_user_exception_dispatcher( EXCEPTION_RECORD *rec, CONTEXT *context ); +@@ -401,7 +407,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) +diff --git a/dlls/webservices/tests/channel.c b/dlls/webservices/tests/channel.c +index 4cf39c10732..1fb12297eef 100644 +--- a/dlls/webservices/tests/channel.c ++++ b/dlls/webservices/tests/channel.c +@@ -1214,6 +1214,9 @@ static const char send_record_begin[] = { + static const char send_record_middle[] = { 0x01, 0x56, 0x0e, 0x42 }; + static const char send_record_end[] = { 0x08, 0x02, 0x6e, 0x73, 0x89, 0xff, 0x01, 0x01 }; + ++#pragma GCC diagnostic ignored "-Warray-bounds" ++#pragma GCC diagnostic ignored "-Wstringop-overflow" ++ + static BOOL send_dict_str( int sock, char *addr, const char *str, int dict_str_count ) + { + char buf[512], dict_buf[256], body_buf[128], dict_size_buf[5]; +diff --git a/include/config.h.in b/include/config.h.in +index cda76fcfe2e..16685419788 100644 +--- a/include/config.h.in ++++ b/include/config.h.in +@@ -174,6 +174,9 @@ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_MAJOR_H + ++/* Define to 1 if you have the header file. */ ++#undef HAVE_LINUX_NTSYNC_H ++ + /* Define to 1 if you have the header file. */ + #undef HAVE_LINUX_PARAM_H + +diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h +index 139a7bca69e..9de93637410 100644 +--- a/include/wine/server_protocol.h ++++ b/include/wine/server_protocol.h +@@ -5637,6 +5637,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 type; ++ unsigned int access; ++ char __pad_20[4]; ++}; ++ ++ ++ ++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, +@@ -5923,6 +6005,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 + }; + +@@ -6214,6 +6301,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 + { +@@ -6503,11 +6595,16 @@ 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 ### */ + +-#define SERVER_PROTOCOL_VERSION 793 ++#define SERVER_PROTOCOL_VERSION 795 + + /* ### protocol_version end ### */ + +diff --git a/server/Makefile.in b/server/Makefile.in +index 7b46b924c46..d21bd5541c7 100644 +--- a/server/Makefile.in ++++ b/server/Makefile.in +@@ -12,6 +12,7 @@ SOURCES = \ + device.c \ + directory.c \ + event.c \ ++ fast_sync.c \ + fd.c \ + file.c \ + handle.c \ +diff --git a/server/async.c b/server/async.c +index 9cb251df5ce..09c5e1ec9a7 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 */ + }; +@@ -688,6 +689,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 843e495411c..20b80f98548 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 */ ++ default_fd_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..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,6 +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 */ ++ completion_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + completion_destroy /* destroy */ + }; +@@ -109,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 ) +@@ -126,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 ) +@@ -138,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; + } + } + +@@ -165,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 */ +@@ -231,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 ); +diff --git a/server/console.c b/server/console.c +index b64283baf4a..17708df7953 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,6 +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 */ ++ console_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_destroy /* destroy */ + }; +@@ -139,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 ); +@@ -149,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 = + { +@@ -170,6 +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 */ ++ console_server_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_server_destroy /* destroy */ + }; +@@ -218,6 +224,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 = + { +@@ -239,6 +246,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 */ ++ screen_buffer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + screen_buffer_destroy /* destroy */ + }; +@@ -288,6 +296,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 */ + }; +@@ -303,6 +312,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 = +@@ -325,6 +335,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 */ ++ console_input_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_input_destroy /* destroy */ + }; +@@ -360,6 +371,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 = +@@ -382,6 +394,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 */ ++ console_output_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + console_output_destroy /* destroy */ + }; +@@ -440,6 +453,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 */ + }; +@@ -539,6 +553,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 ); + +@@ -578,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; + } +@@ -610,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 ); + } + } + +@@ -764,6 +781,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 ) +@@ -811,6 +830,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; +@@ -860,6 +889,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 ); +@@ -872,6 +912,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, +@@ -926,6 +967,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; +@@ -937,6 +989,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) + { +@@ -1402,6 +1455,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; +@@ -1474,6 +1537,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; +@@ -1531,11 +1604,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) +@@ -1557,6 +1635,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) +@@ -1643,5 +1723,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 ); + } +diff --git a/server/debugger.c b/server/debugger.c +index 48adb244b09..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 */ + }; + + +@@ -98,12 +99,14 @@ 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 */ + }; + + 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 = +@@ -126,6 +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 */ ++ debug_obj_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + debug_obj_destroy /* destroy */ + }; +@@ -253,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 ); + } + } +@@ -265,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 ); + } + } +@@ -330,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; +@@ -342,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 ) +@@ -361,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; +@@ -569,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 + { +diff --git a/server/device.c b/server/device.c +index 436dac6bfe9..698fee63f03 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 */ + }; +@@ -92,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 = +@@ -118,6 +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 */ ++ device_manager_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + device_manager_destroy /* destroy */ + }; +@@ -175,6 +179,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 +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 */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + device_file_close_handle, /* close_handle */ + device_file_destroy /* destroy */ + }; +@@ -417,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, +@@ -751,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 ); + } + +@@ -782,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; +@@ -816,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) +@@ -825,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 ); +@@ -1014,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; +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..b750a22487b 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,8 +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 */ ++ event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ event_destroy /* destroy */ + }; + + +@@ -106,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 = + { +@@ -131,8 +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 */ ++ keyed_event_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ keyed_event_destroy /* destroy */ + }; + + +@@ -150,6 +158,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; +@@ -173,11 +182,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 ) +@@ -223,6 +234,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 ) + { +@@ -233,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; +@@ -276,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) + { +diff --git a/server/fast_sync.c b/server/fast_sync.c +new file mode 100644 +index 00000000000..8684eb6dcd7 +--- /dev/null ++++ b/server/fast_sync.c +@@ -0,0 +1,420 @@ ++/* ++ * Fast synchronization primitives ++ * ++ * Copyright (C) 2021-2022 Elizabeth 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 "handle.h" ++#include "request.h" ++#include "thread.h" ++ ++#ifdef HAVE_LINUX_NTSYNC_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 fast_sync_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 fast_sync_fd_ops = ++{ ++ default_fd_get_poll_events, /* get_poll_events */ ++ default_poll_event, /* poll_event */ ++ fast_sync_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 fast_sync_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 (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 ); ++ ++ unix_fd = open( "/dev/ntsync", O_CLOEXEC | O_RDONLY ); ++ if (unix_fd == -1) ++ { ++ file_set_error(); ++ 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( &fast_sync_fd_ops, unix_fd, &device->obj, 0 ))) ++ { ++ release_object( device ); ++ return NULL; ++ } ++ ++ linux_device_object = device; ++ return device; ++} ++ ++struct fast_sync ++{ ++ struct object obj; ++ enum fast_sync_type type; ++ struct fd *fd; ++}; ++ ++static void linux_obj_dump( struct object *obj, int verbose ); ++static void linux_obj_destroy( struct object *obj ); ++static struct fd *linux_obj_get_fd( struct object *obj ); ++ ++static const struct object_ops linux_obj_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 */ ++ linux_obj_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 == &linux_obj_ops ); ++ fprintf( stderr, "Fast synchronization object type=%u fd=%p\n", fast_sync->type, fast_sync->fd ); ++} ++ ++static void linux_obj_destroy( struct object *obj ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ assert( obj->ops == &linux_obj_ops ); ++ if (fast_sync->fd) release_object( fast_sync->fd ); ++} ++ ++static struct fd *linux_obj_get_fd( struct object *obj ) ++{ ++ struct fast_sync *fast_sync = (struct fast_sync *)obj; ++ assert( obj->ops == &linux_obj_ops ); ++ return (struct fd *)grab_object( fast_sync->fd ); ++} ++ ++static struct fast_sync *create_fast_sync( enum fast_sync_type type, int unix_fd ) ++{ ++ struct fast_sync *fast_sync; ++ ++ if (!(fast_sync = alloc_object( &linux_obj_ops ))) ++ { ++ close( unix_fd ); ++ return NULL; ++ } ++ ++ fast_sync->type = type; ++ ++ if (!(fast_sync->fd = create_anonymous_fd( &fast_sync_fd_ops, unix_fd, &fast_sync->obj, 0 ))) ++ { ++ release_object( fast_sync ); ++ return NULL; ++ } ++ ++ return fast_sync; ++} ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ struct ntsync_event_args args = {0}; ++ struct linux_device *device; ++ ++ 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 ), NTSYNC_IOC_CREATE_EVENT, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ release_object( device ); ++ ++ return create_fast_sync( type, args.event ); ++} ++ ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ struct ntsync_sem_args args = {0}; ++ struct linux_device *device; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.count = count; ++ args.max = max; ++ if (ioctl( get_unix_fd( device->fd ), NTSYNC_IOC_CREATE_SEM, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ release_object( device ); ++ ++ return create_fast_sync( FAST_SYNC_SEMAPHORE, args.sem ); ++} ++ ++struct fast_sync *fast_create_mutex( thread_id_t owner, unsigned int count ) ++{ ++ struct ntsync_mutex_args args = {0}; ++ struct linux_device *device; ++ ++ if (!(device = get_linux_device())) return NULL; ++ ++ args.owner = owner; ++ args.count = count; ++ if (ioctl( get_unix_fd( device->fd ), NTSYNC_IOC_CREATE_MUTEX, &args ) < 0) ++ { ++ file_set_error(); ++ release_object( device ); ++ return NULL; ++ } ++ ++ release_object( device ); ++ ++ return create_fast_sync( FAST_SYNC_MUTEX, args.mutex ); ++} ++ ++void fast_set_event( struct fast_sync *fast_sync ) ++{ ++ __u32 count; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_set_event %p\n", fast_sync->fd ); ++ ++ ioctl( get_unix_fd( fast_sync->fd ), NTSYNC_IOC_EVENT_SET, &count ); ++} ++ ++void fast_reset_event( struct fast_sync *fast_sync ) ++{ ++ __u32 count; ++ ++ if (!fast_sync) return; ++ ++ if (debug_level) fprintf( stderr, "fast_set_event %p\n", fast_sync->fd ); ++ ++ ioctl( get_unix_fd( fast_sync->fd ), NTSYNC_IOC_EVENT_RESET, &count ); ++} ++ ++void fast_abandon_mutex( thread_id_t tid, struct fast_sync *fast_sync ) ++{ ++ ioctl( get_unix_fd( fast_sync->fd ), NTSYNC_IOC_MUTEX_KILL, &tid ); ++} ++ ++#else ++ ++struct fast_sync *fast_create_event( enum fast_sync_type type, int signaled ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ return NULL; ++} ++ ++struct fast_sync *fast_create_semaphore( unsigned int count, unsigned int max ) ++{ ++ set_error( STATUS_NOT_IMPLEMENTED ); ++ 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 ) ++{ ++} ++ ++void fast_reset_event( struct fast_sync *obj ) ++{ ++} ++ ++void fast_abandon_mutex( thread_id_t tid, struct fast_sync *fast_sync ) ++{ ++} ++ ++#endif ++ ++DECL_HANDLER(get_linux_sync_device) ++{ ++#ifdef HAVE_LINUX_NTSYNC_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 ++} ++ ++DECL_HANDLER(get_linux_sync_obj) ++{ ++#ifdef HAVE_LINUX_NTSYNC_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 ))) ++ { ++ 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->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/fd.c b/server/fd.c +index 8576882aaa9..39b94fd283a 100644 +--- a/server/fd.c ++++ b/server/fd.c +@@ -156,6 +156,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 ); +@@ -181,6 +182,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 */ + }; +@@ -222,6 +224,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 */ + }; +@@ -262,6 +265,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 */ + }; +@@ -304,6 +308,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 */ + }; +@@ -1565,6 +1570,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 */ +@@ -1683,6 +1689,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 ); +@@ -1723,6 +1730,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 ); +@@ -2139,7 +2147,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) */ +@@ -2165,6 +2181,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 76c687833c9..1191303c35a 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 */ ++ 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 39a833cd105..69f5df9b463 100644 +--- a/server/file.h ++++ b/server/file.h +@@ -108,6 +108,7 @@ extern char *dup_fd_name( struct fd *root, const char *name ) __WINE_DEALLOC(fre + 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/handle.c b/server/handle.c +index 0595fdb403b..17d3617f0a3 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..d9807b4ad23 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 */ ++ default_fd_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 */ ++ default_fd_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 f754078acf7..23901aa6f76 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,6 +190,7 @@ static const struct object_ops mapping_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 */ + mapping_destroy /* destroy */ + }; +diff --git a/server/mutex.c b/server/mutex.c +index af0efe72132..167c236e014 100644 +--- a/server/mutex.c ++++ b/server/mutex.c +@@ -38,6 +38,8 @@ + + static const WCHAR mutex_name[] = {'M','u','t','a','n','t'}; + ++static struct list fast_mutexes = LIST_INIT(fast_mutexes); ++ + struct type_descr mutex_type = + { + { mutex_name, sizeof(mutex_name) }, /* name */ +@@ -57,6 +59,8 @@ struct mutex + unsigned int count; /* recursion count */ + int abandoned; /* has it been abandoned? */ + struct list entry; /* entry in owner thread mutex list */ ++ struct list fast_mutexes_entry; /* entry in fast_mutexes list */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ + }; + + static void mutex_dump( struct object *obj, int verbose ); +@@ -64,6 +68,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,6 +90,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 */ ++ mutex_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + mutex_destroy /* destroy */ + }; +@@ -127,6 +133,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; +@@ -134,16 +141,22 @@ static struct mutex *create_mutex( struct object *root, const struct unicode_str + + void abandon_mutexes( struct thread *thread ) + { ++ struct mutex *mutex; + struct list *ptr; + + while ((ptr = list_head( &thread->mutex_list )) != NULL) + { +- struct mutex *mutex = LIST_ENTRY( ptr, struct mutex, entry ); ++ mutex = LIST_ENTRY( ptr, struct mutex, entry ); + assert( mutex->owner == thread ); + mutex->count = 0; + mutex->abandoned = 1; + do_release( mutex ); + } ++ ++ LIST_FOR_EACH_ENTRY(mutex, &fast_mutexes, struct mutex, fast_mutexes_entry) ++ { ++ fast_abandon_mutex( thread->id, mutex->fast_sync ); ++ } + } + + static void mutex_dump( struct object *obj, int verbose ) +@@ -189,14 +202,34 @@ 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) list_add_tail( &fast_mutexes, &mutex->fast_mutexes_entry ); ++ } ++ 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 ); ++ list_remove( &mutex->fast_mutexes_entry ); ++ } + } + + /* create a mutex */ +diff --git a/server/named_pipe.c b/server/named_pipe.c +index f3404a33c3b..6d8cb3ea350 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 */ ++ default_fd_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 */ ++ default_fd_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 */ ++ default_fd_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 89e541ffb6b..6c6568e5d24 100644 +--- a/server/object.c ++++ b/server/object.c +@@ -538,6 +538,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 dfdd691601f..f1f39571136 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,17 @@ extern void reset_event( struct event *event ); + + 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 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_mutex( thread_id_t tid, struct fast_sync *fast_sync ); ++ ++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 a0d5ea64d97..f61916d3adf 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,6 +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 */ ++ process_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + process_destroy /* destroy */ + }; +@@ -168,6 +170,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 */ + }; +@@ -190,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 ); + +@@ -207,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 = +@@ -229,6 +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 */ ++ job_get_fast_sync, /* get_fast_sync */ + job_close_handle, /* close_handle */ + job_destroy /* destroy */ + }; +@@ -253,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; +@@ -409,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 ) +@@ -439,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 ) +@@ -682,6 +702,7 @@ struct process *create_process( int fd, struct process *parent, unsigned int fla + process->rawinput_device_count = 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 ); +@@ -786,6 +807,8 @@ static void process_destroy( struct object *obj ) + free( process->rawinput_devices ); + free( process->dir_cache ); + free( process->image ); ++ ++ if (process->fast_sync) release_object( process->fast_sync ); + } + + /* dump a process on stdout for debugging purposes */ +@@ -817,6 +840,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; +@@ -981,6 +1014,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 97e0d455ece..6ec97ae4527 100644 +--- a/server/process.h ++++ b/server/process.h +@@ -85,6 +85,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 */ +diff --git a/server/protocol.def b/server/protocol.def +index 5d60e7fcda3..38b0d9d062e 100644 +--- a/server/protocol.def ++++ b/server/protocol.def +@@ -3884,3 +3884,52 @@ 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, ++}; ++ ++ ++/* Obtain a handle to the fast synchronization device object */ ++@REQ(get_linux_sync_device) ++@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 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 ++ ++ ++/* 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/queue.c b/server/queue.c +index cd913ae03e5..7afa1dce898 100644 +--- a/server/queue.c ++++ b/server/queue.c +@@ -142,6 +142,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 */ ++ struct fast_sync *fast_sync; /* fast synchronization object */ ++ int in_fast_wait; /* are we in a client-side wait? */ + }; + + struct hotkey +@@ -159,6 +161,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 ); +@@ -185,6 +188,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 */ ++ msg_queue_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + msg_queue_destroy /* destroy */ + }; +@@ -222,6 +226,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 */ + }; +@@ -310,6 +315,8 @@ 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; ++ queue->in_fast_wait = 0; + list_init( &queue->send_result ); + list_init( &queue->callback_result ); + list_init( &queue->pending_timers ); +@@ -591,7 +598,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 */ +@@ -604,6 +615,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 if message is matched by the filter */ +@@ -1082,6 +1095,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; + } + +@@ -1142,6 +1159,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 ) +@@ -1181,6 +1209,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 ) +@@ -1191,6 +1220,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 ) +@@ -2556,8 +2586,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 ); + } + } + } +@@ -2572,6 +2614,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; + } +@@ -2753,6 +2797,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 )) +@@ -2811,6 +2858,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 */ + } + +@@ -3508,3 +3556,56 @@ DECL_HANDLER(update_rawinput_devices) + process->rawinput_mouse = find_rawinput_device( process, 1, 2 ); + process->rawinput_kbd = find_rawinput_device( process, 1, 6 ); + } ++ ++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 ); ++} +diff --git a/server/registry.c b/server/registry.c +index da6a6d0982e..83d36580356 100644 +--- a/server/registry.c ++++ b/server/registry.c +@@ -192,6 +192,7 @@ static const struct object_ops key_ops = + key_unlink_name, /* 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/request.h b/server/request.h +index 89d5a621b16..e255b8206e2 100644 +--- a/server/request.h ++++ b/server/request.h +@@ -403,6 +403,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 + +@@ -693,6 +698,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 ); +@@ -2336,6 +2346,23 @@ 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, type) == 12 ); ++C_ASSERT( FIELD_OFFSET(struct get_linux_sync_obj_reply, access) == 16 ); ++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/semaphore.c b/server/semaphore.c +index 53b42a886df..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,8 +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 */ ++ semaphore_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ +- no_destroy /* destroy */ ++ semaphore_destroy /* destroy */ + }; + + +@@ -105,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; +@@ -167,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) + { +diff --git a/server/serial.c b/server/serial.c +index d665eb7fa35..5c210d10a80 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 */ ++ default_fd_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 c34fd3eb5eb..c226d7d0162 100644 +--- a/server/sock.c ++++ b/server/sock.c +@@ -465,6 +465,7 @@ static const struct object_ops sock_ops = + NULL, /* unlink_name */ + no_open_file, /* open_file */ + no_kernel_obj_list, /* get_kernel_obj_list */ ++ default_fd_get_fast_sync, /* get_fast_sync */ + sock_close_handle, /* close_handle */ + sock_destroy /* destroy */ + }; +@@ -3566,6 +3567,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 */ + }; +@@ -3787,6 +3789,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 dd28efd3a75..4a7cf68f269 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 56f57cefd8f..13585ee9d34 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 */ + }; +@@ -150,6 +151,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 */ + }; +@@ -177,6 +179,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 = +@@ -199,6 +202,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 */ ++ thread_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + destroy_thread /* destroy */ + }; +@@ -246,6 +250,8 @@ static inline void init_thread_structure( struct thread *thread ) + thread->token = NULL; + 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; +@@ -396,6 +402,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 ) +@@ -450,6 +466,8 @@ 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 ); ++ if (thread->fast_alert_event) release_object( thread->fast_alert_event ); + } + + /* dump a thread on stdout for debugging purposes */ +@@ -1143,8 +1161,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; + } + +@@ -1175,6 +1198,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; + } + } +@@ -1189,6 +1214,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; + } +@@ -1286,6 +1314,7 @@ void kill_thread( struct thread *thread, int violent_death ) + kill_console_processes( thread, 0 ); + abandon_mutexes( thread ); + 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 ); +@@ -2028,3 +2057,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 8dcf966a90a..9586138640b 100644 +--- a/server/thread.h ++++ b/server/thread.h +@@ -90,6 +90,8 @@ 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 */ ++ struct event *fast_alert_event; /* fast synchronization alert event */ + }; + + extern struct thread *current; +diff --git a/server/timer.c b/server/timer.c +index 96dc9d00ca1..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,6 +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 */ ++ timer_get_fast_sync, /* get_fast_sync */ + no_close_handle, /* close_handle */ + timer_destroy /* destroy */ + }; +@@ -110,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; +@@ -151,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 */ +@@ -181,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; +@@ -215,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; +@@ -222,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 */ +diff --git a/server/token.c b/server/token.c +index 4df8d2e0c6e..42562fdf88c 100644 +--- a/server/token.c ++++ b/server/token.c +@@ -155,6 +155,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/trace.c b/server/trace.c +index 1b65d2b977e..a5a9fec5700 100644 +--- a/server/trace.c ++++ b/server/trace.c +@@ -4606,6 +4606,47 @@ 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, ", 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, +@@ -4891,6 +4932,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] = { +@@ -5178,6 +5224,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] = { +@@ -5465,6 +5516,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 +diff --git a/server/window.c b/server/window.c +index 242e93f303a..8ddf78e54b8 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 5903497d61e..cabc93fa571 100644 +--- a/server/winstation.c ++++ b/server/winstation.c +@@ -88,6 +88,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 */ + }; +@@ -128,6 +129,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 */ + }; diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/legacy/proton-cpu-topology-overrides-e05c6c8.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/legacy/proton-cpu-topology-overrides-e05c6c8.patch new file mode 100644 index 000000000..810617afa --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-cpu-topology-overrides/legacy/proton-cpu-topology-overrides-e05c6c8.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 ); + extern void init_files(void); + extern void init_cpu_info(void); ++extern struct cpu_topology_override *get_cpu_topology_override(void); + extern void add_completion( HANDLE handle, ULONG_PTR value, NTSTATUS status, ULONG info, BOOL async ); + + extern void dbg_init(void); + +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 884bbe80c..932f9e15c 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,10 @@ # 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 59485f00c917061c097c1805d7fa7f61c380c749 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor e05c6c821104d45344fc532a15f96360a24a0174 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 59485f00c917061c097c1805d7fa7f61c380c749 HEAD ); then + _patchname='proton-cpu-topology-overrides-e05c6c8.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 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 810617afa..3daebba59 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 @@ -65,7 +65,7 @@ index b1d3e863240..7b0c7c32f58 100644 @@ -1467,6 +1467,7 @@ void CDECL server_init_process_done( void *relay ) void server_init_process_done(void) { - void *entry, *teb; + void *teb; + struct cpu_topology_override *cpu_override = get_cpu_topology_override(); unsigned int status; int suspend; @@ -447,9 +447,9 @@ index 97e074a5261..d1f9ab764f7 100644 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; + + if (is_process_init_done(process)) + { @@ -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 ); diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-ea890c4.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-ea890c4.patch new file mode 100644 index 000000000..78aa04770 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/legacy/proton-tkg-ea890c4.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 NTSTATUS WINAPI call_vulkan_debug_report_callback( void *args, ULONG size ) + { + 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 +@@ -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/proton-tkg.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/mainline/proton-tkg.patch index 78aa04770..b425c7846 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 @@ -2892,9 +2892,9 @@ index 516c817715f..a0287c1ff09 100755 "vkFreeCommandBuffers", "vkFreeMemory", - "vkGetCalibratedTimestampsEXT", + "vkGetCalibratedTimestampsKHR", "vkGetDeviceProcAddr", "vkGetDeviceQueue", - "vkGetDeviceQueue2", @@ -285,6 +285,7 @@ MANUAL_LOADER_THUNKS = { "vkEnumerateInstanceExtensionProperties", "vkEnumerateInstanceVersion", 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 25c8ad4c2..851b3a26c 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 643538a836f33b4996730cf05dc2af77667d6150 HEAD ); then + if ( cd "${srcdir}"/"${_winesrcdir}" && git merge-base --is-ancestor ea890c4733db5119fdb63b900bb7e5c8d04b5245 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,11 @@ fi fi else - if git merge-base --is-ancestor 3681a68e7804e0398b3f15f16af4ae19ef2e5dba HEAD; then + if git merge-base --is-ancestor 643538a836f33b4996730cf05dc2af77667d6150 HEAD; then + _lastcommit="ea890c4" + _rpc="1" + _stmbits="1" + elif git merge-base --is-ancestor 3681a68e7804e0398b3f15f16af4ae19ef2e5dba HEAD; then _lastcommit="643538a" _rpc="1" _stmbits="1" diff --git a/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-ea890c4.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-ea890c4.patch new file mode 100644 index 000000000..6583ae153 --- /dev/null +++ b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/legacy/proton-tkg-staging-ea890c4.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 NTSTATUS WINAPI call_vulkan_debug_report_callback( void *args, ULONG size ) + { + 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 +@@ -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/proton-tkg-staging.patch b/wine-tkg-git/wine-tkg-patches/proton-tkg-specific/proton-tkg/staging/proton-tkg-staging.patch index 6583ae153..72e3b0014 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 @@ -2831,9 +2831,9 @@ index 516c817715f..a0287c1ff09 100755 "vkFreeCommandBuffers", "vkFreeMemory", - "vkGetCalibratedTimestampsEXT", + "vkGetCalibratedTimestampsKHR", "vkGetDeviceProcAddr", "vkGetDeviceQueue", - "vkGetDeviceQueue2", @@ -285,6 +285,7 @@ MANUAL_LOADER_THUNKS = { "vkEnumerateInstanceExtensionProperties", "vkEnumerateInstanceVersion", 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 29c82b440..f6fb7c641 100644 --- a/wine-tkg-git/wine-tkg-profiles/sample-external-config.cfg +++ b/wine-tkg-git/wine-tkg-profiles/sample-external-config.cfg @@ -162,6 +162,12 @@ _configure_userargs32="--with-x --with-gstreamer --with-xattr" # !! For plain Wine required disabling esync and fsync patches applying !! _use_fastsync="false" +# NTsync5 - https://repo.or.cz/wine/zf.git/shortlog/refs/heads/ntsync5 +# !! For building and using requires ntsync module and headers (see the three packages https://aur.archlinux.org/pkgbase/ntsync) !! +# !! Not compatible with _protonify, _use_staging, nor any of _use_esync, _use_fsync or _use_fastsync options at this time !! +# !! Not compatible with Valve trees !! +_use_ntsync="false" + # esync - Enable with WINEESYNC=1 envvar - Set to true to enable esync support on plain wine or wine-staging <4.6 (it got merged in wine-staging 4.6). The option is ignored on wine-staging 4.6+ # You may need to raise your fd limits -> https://github.com/zfigura/wine/blob/esync/README.esync _use_esync="true" diff --git a/wine-tkg-git/wine-tkg-scripts/deps b/wine-tkg-git/wine-tkg-scripts/deps index 5045c324e..babefced0 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 jq + ${_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 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 @@ -19,7 +19,7 @@ _debuntu_32() { # 32-bit msg2 "" msg2 "Installing 32-bit dependencies for Debian-based distros..." - ${_as_root}apt install libunwind-dev:i386 xserver-xorg-dev:i386 libfreetype6-dev:i386 libfontconfig1-dev:i386 libglu1-mesa-dev:i386 libglu1-mesa:i386 libgl1-mesa-dev:i386 libgl1:i386 libosmesa6-dev:i386 libosmesa6:i386 mesa-common-dev:i386 libegl1-mesa-dev:i386 libegl-dev:i386 libgl-dev:i386 libglx-dev:i386 libglx0:i386 libllvm12:i386 libgles-dev:i386 libglvnd-dev:i386 libgles2-mesa-dev:i386 libvulkan-dev:i386 libvulkan1:i386 libpulse-dev:i386 libopenal-dev:i386 libncurses-dev:i386 libfaudio0:i386 libfaudio-dev:i386 libvkd3d-dev:i386 libgnutls28-dev:i386 libtiff-dev:i386 libldap-dev:i386 libcapi20-dev:i386 libpcap-dev:i386 libxml2-dev:i386 libmpg123-dev:i386 libgphoto2-dev:i386 libsane-dev:i386 libcupsimage2-dev:i386 libgsm1-dev:i386 libxslt1-dev:i386 libv4l-dev:i386 libudev-dev:i386 libxi-dev:i386 liblcms2-dev:i386 libibus-1.0-dev:i386 libsdl2-dev:i386 ocl-icd-opencl-dev:i386 libxinerama-dev:i386 libxcursor-dev:i386 libxrandr-dev:i386 libxcomposite-dev:i386 libavcodec58:i386 libswresample3:i386 libavutil56:i386 valgrind:i386 libgcrypt20-dev:i386 samba-libs:i386 python3-ldb:i386 python3-talloc:i386 python3:i386 samba-dev:i386 libusb-1.0-0-dev:i386 libgstreamer1.0-dev:i386 libgstreamer-plugins-base1.0-dev:i386 + ${_as_root}apt install libunwind-dev:i386 xserver-xorg-dev:i386 libfreetype6-dev:i386 libfontconfig1-dev:i386 libglu1-mesa-dev:i386 libglu1-mesa:i386 libgl1-mesa-dev:i386 libgl1:i386 libosmesa6-dev:i386 libosmesa6:i386 mesa-common-dev:i386 libegl1-mesa-dev:i386 libegl-dev:i386 libgl-dev:i386 libglx-dev:i386 libglx0:i386 libllvm12:i386 libgles-dev:i386 libglvnd-dev:i386 libgles2-mesa-dev:i386 libvulkan-dev:i386 libvulkan1:i386 libpulse-dev:i386 libopenal-dev:i386 libncurses-dev:i386 libvkd3d-dev:i386 libgnutls28-dev:i386 libtiff-dev:i386 libldap-dev:i386 libcapi20-dev:i386 libpcap-dev:i386 libxml2-dev:i386 libmpg123-dev:i386 libgphoto2-dev:i386 libsane-dev:i386 libcupsimage2-dev:i386 libgsm1-dev:i386 libxslt1-dev:i386 libv4l-dev:i386 libudev-dev:i386 libxi-dev:i386 liblcms2-dev:i386 libibus-1.0-dev:i386 libsdl2-dev:i386 ocl-icd-opencl-dev:i386 libxinerama-dev:i386 libxcursor-dev:i386 libxrandr-dev:i386 libxcomposite-dev:i386 libavcodec58:i386 libswresample3:i386 libavutil56:i386 valgrind:i386 libgcrypt20-dev:i386 samba-libs:i386 python3-ldb:i386 python3-talloc:i386 python3:i386 samba-dev:i386 libusb-1.0-0-dev:i386 libgstreamer1.0-dev:i386 libgstreamer-plugins-base1.0-dev:i386 } _fedora_64() { @@ -28,7 +28,7 @@ _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 jq + ${_as_root}dnf install git make ccache gcc-c++ mingw32-gcc mingw64-gcc SDL2-devel openal-soft-devel opencl-headers ocl-icd-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 } @@ -38,7 +38,7 @@ _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 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 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 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 } @@ -48,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 jq + ${_as_root}pacman -S --needed attr fontconfig lcms2 libxml2 libxcursor libxrandr libxdamage libxi gettext freetype2 glu libsm gcc-libs libpcap 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() { @@ -56,5 +56,5 @@ _archlinux_32() { # 32-bit msg2 "" msg2 "Installing 32-bit dependencies for Archlinux based distros..." - ${_as_root}pacman -S --needed lib32-attr lib32-fontconfig 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 lib32-faudio lib32-ncurses 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-giflib lib32-libpng lib32-alsa-plugins lib32-libjpeg-turbo lib32-rust-libs lib32-libgcrypt + ${_as_root}pacman -S --needed lib32-attr lib32-fontconfig 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 lib32-ncurses 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-giflib lib32-libpng lib32-alsa-plugins lib32-libjpeg-turbo lib32-rust-libs lib32-libgcrypt }