diff --git a/configure.ac b/configure.ac index 8e4462b1d4c..22229880023 100644 --- a/configure.ac +++ b/configure.ac @@ -1994,6 +1994,7 @@ AC_CHECK_FUNCS(\ getrandom \ kqueue \ mach_continuous_time \ + memfd_create \ pipe2 \ port_create \ posix_fadvise \ @@ -3579,6 +3580,7 @@ WINE_CONFIG_MAKEFILE(po) WINE_CONFIG_MAKEFILE(programs/arp) WINE_CONFIG_MAKEFILE(programs/aspnet_regiis) WINE_CONFIG_MAKEFILE(programs/attrib) +WINE_CONFIG_MAKEFILE(programs/belauncher) WINE_CONFIG_MAKEFILE(programs/cabarc) WINE_CONFIG_MAKEFILE(programs/cacls) WINE_CONFIG_MAKEFILE(programs/chcp.com) diff --git a/dlls/amd_ags_x64/amd_ags.h b/dlls/amd_ags_x64/amd_ags.h index 20fef455f00..9b521de75b8 100644 --- a/dlls/amd_ags_x64/amd_ags.h +++ b/dlls/amd_ags_x64/amd_ags.h @@ -1506,8 +1506,10 @@ AMD_AGS_API AGSReturnCode agsDriverExtensionsDX11_EndUAVOverlap( AGSContext* con /// \param [in] minDepth The near depth range to clip against. /// \param [in] maxDepth The far depth range to clip against. /// -AMD_AGS_API AGSReturnCode agsDriverExtensionsDX11_SetDepthBounds_520( AGSContext* context, bool enabled, float minDepth, float maxDepth ); -AMD_AGS_API AGSReturnCode agsDriverExtensionsDX11_SetDepthBounds( AGSContext* context, ID3D11DeviceContext* dxContext, bool enabled, float minDepth, float maxDepth ); +AMD_AGS_API AGSReturnCode agsDriverExtensionsDX11_SetDepthBounds( AGSContext* context, bool enabled, float minDepth, float maxDepth ); + +/* Since 5.3.0 */ +AMD_AGS_API AGSReturnCode agsDriverExtensionsDX11_SetDepthBounds_530( AGSContext* context, ID3D11DeviceContext* dxContext, bool enabled, float minDepth, float maxDepth ); /// @} diff --git a/dlls/amd_ags_x64/amd_ags_x64.spec b/dlls/amd_ags_x64/amd_ags_x64.spec index ebb026da35e..3d9800469a1 100644 --- a/dlls/amd_ags_x64/amd_ags_x64.spec +++ b/dlls/amd_ags_x64/amd_ags_x64.spec @@ -3,14 +3,14 @@ @ stdcall agsCheckDriverVersion(ptr long) @ stub agsDriverExtensionsDX11_BeginUAVOverlap @ stub agsDriverExtensionsDX11_CreateBuffer -@ stub agsDriverExtensionsDX11_CreateDevice +@ stdcall agsDriverExtensionsDX11_CreateDevice(ptr ptr ptr ptr) @ stub agsDriverExtensionsDX11_CreateFromDevice @ stub agsDriverExtensionsDX11_CreateTexture1D @ stub agsDriverExtensionsDX11_CreateTexture2D @ stub agsDriverExtensionsDX11_CreateTexture3D @ stub agsDriverExtensionsDX11_DeInit @ stub agsDriverExtensionsDX11_Destroy -@ stub agsDriverExtensionsDX11_DestroyDevice +@ stdcall -norelay -arch=win64 agsDriverExtensionsDX11_DestroyDevice() @ stub agsDriverExtensionsDX11_EndUAVOverlap @ stub agsDriverExtensionsDX11_GetMaxClipRects @ stub agsDriverExtensionsDX11_IASetPrimitiveTopology @@ -24,7 +24,7 @@ @ stub agsDriverExtensionsDX11_NotifyResourceEndWrites @ stub agsDriverExtensionsDX11_NumPendingAsyncCompileJobs @ stub agsDriverExtensionsDX11_SetClipRects -@ stub agsDriverExtensionsDX11_SetDepthBounds +@ stdcall -norelay -arch=win64 agsDriverExtensionsDX11_SetDepthBounds() DX11_SetDepthBounds_impl @ stub agsDriverExtensionsDX11_SetDiskShaderCacheEnabled @ stub agsDriverExtensionsDX11_SetMaxAsyncCompileThreadCount @ stub agsDriverExtensionsDX11_SetViewBroadcastMasks diff --git a/dlls/amd_ags_x64/amd_ags_x64_main.c b/dlls/amd_ags_x64/amd_ags_x64_main.c index 24b16021acf..b58926c928c 100644 --- a/dlls/amd_ags_x64/amd_ags_x64_main.c +++ b/dlls/amd_ags_x64/amd_ags_x64_main.c @@ -8,6 +8,7 @@ #include "wine/heap.h" #include "wine/vulkan.h" +#include "wine/asm.h" #define COBJMACROS #include "d3d11.h" @@ -38,18 +39,19 @@ static const struct int minor; int patch; unsigned int device_size; + unsigned int dx11_returned_params_size; } amd_ags_info[AMD_AGS_VERSION_COUNT] = { - {5, 1, 1, sizeof(AGSDeviceInfo_511)}, - {5, 2, 0, sizeof(AGSDeviceInfo_520)}, - {5, 2, 1, sizeof(AGSDeviceInfo_520)}, - {5, 3, 0, sizeof(AGSDeviceInfo_520)}, - {5, 4, 0, sizeof(AGSDeviceInfo_540)}, - {5, 4, 1, sizeof(AGSDeviceInfo_541)}, - {5, 4, 2, sizeof(AGSDeviceInfo_542)}, - {6, 0, 0, sizeof(AGSDeviceInfo_600)}, - {6, 0, 1, sizeof(AGSDeviceInfo_600)}, + {5, 1, 1, sizeof(AGSDeviceInfo_511), sizeof(AGSDX11ReturnedParams_511)}, + {5, 2, 0, sizeof(AGSDeviceInfo_520), sizeof(AGSDX11ReturnedParams_520)}, + {5, 2, 1, sizeof(AGSDeviceInfo_520), sizeof(AGSDX11ReturnedParams_520)}, + {5, 3, 0, sizeof(AGSDeviceInfo_520), sizeof(AGSDX11ReturnedParams_520)}, + {5, 4, 0, sizeof(AGSDeviceInfo_540), sizeof(AGSDX11ReturnedParams_520)}, + {5, 4, 1, sizeof(AGSDeviceInfo_541), sizeof(AGSDX11ReturnedParams_520)}, + {5, 4, 2, sizeof(AGSDeviceInfo_542), sizeof(AGSDX11ReturnedParams_520)}, + {6, 0, 0, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600)}, + {6, 0, 1, sizeof(AGSDeviceInfo_600), sizeof(AGSDX11ReturnedParams_600)}, }; #define DEF_FIELD(name) {DEVICE_FIELD_##name, {offsetof(AGSDeviceInfo_511, name), offsetof(AGSDeviceInfo_520, name), \ @@ -117,8 +119,10 @@ struct AGSContext VkPhysicalDeviceMemoryProperties *memory_properties; }; -static HMODULE hd3d12; +static HMODULE hd3d11, hd3d12; static typeof(D3D12CreateDevice) *pD3D12CreateDevice; +static typeof(D3D11CreateDevice) *pD3D11CreateDevice; +static typeof(D3D11CreateDeviceAndSwapChain) *pD3D11CreateDeviceAndSwapChain; static BOOL load_d3d12_functions(void) { @@ -132,6 +136,19 @@ static BOOL load_d3d12_functions(void) return TRUE; } +static BOOL load_d3d11_functions(void) +{ + if (hd3d11) + return TRUE; + + if (!(hd3d11 = LoadLibraryA("d3d11.dll"))) + return FALSE; + + pD3D11CreateDevice = (void *)GetProcAddress(hd3d11, "D3D11CreateDevice"); + pD3D11CreateDeviceAndSwapChain = (void *)GetProcAddress(hd3d11, "D3D11CreateDeviceAndSwapChain"); + return TRUE; +} + static AGSReturnCode vk_get_physical_device_properties(unsigned int *out_count, VkPhysicalDeviceProperties **out, VkPhysicalDeviceMemoryProperties **out_memory) { @@ -605,6 +622,76 @@ AGSReturnCode WINAPI agsGetCrossfireGPUCount(AGSContext *context, int *gpu_count return AGS_SUCCESS; } +AGSReturnCode WINAPI agsDriverExtensionsDX11_CreateDevice( AGSContext* context, + const AGSDX11DeviceCreationParams* creation_params, const AGSDX11ExtensionParams* extension_params, + AGSDX11ReturnedParams* returned_params ) +{ + ID3D11DeviceContext *device_context; + IDXGISwapChain *swapchain = NULL; + D3D_FEATURE_LEVEL feature_level; + ID3D11Device *device; + HRESULT hr; + + TRACE("feature levels %u, pSwapChainDesc %p, app %s, engine %s %#x %#x.\n", creation_params->FeatureLevels, + creation_params->pSwapChainDesc, + debugstr_w(extension_params->agsDX11ExtensionParams511.pAppName), + debugstr_w(extension_params->agsDX11ExtensionParams511.pEngineName), + extension_params->agsDX11ExtensionParams511.appVersion, + extension_params->agsDX11ExtensionParams511.engineVersion); + + if (!load_d3d11_functions()) + { + ERR("Could not load d3d11.dll.\n"); + return AGS_MISSING_D3D_DLL; + } + memset( returned_params, 0, amd_ags_info[context->version].dx11_returned_params_size ); + if (creation_params->pSwapChainDesc) + { + hr = pD3D11CreateDeviceAndSwapChain(creation_params->pAdapter, creation_params->DriverType, + creation_params->Software, creation_params->Flags, creation_params->pFeatureLevels, + creation_params->FeatureLevels, creation_params->SDKVersion, creation_params->pSwapChainDesc, + &swapchain, &device, &feature_level, &device_context); + } + else + { + hr = pD3D11CreateDevice(creation_params->pAdapter, creation_params->DriverType, + creation_params->Software, creation_params->Flags, creation_params->pFeatureLevels, + creation_params->FeatureLevels, creation_params->SDKVersion, + &device, &feature_level, &device_context); + } + if (FAILED(hr)) + { + ERR("Device creation failed, hr %#x.\n", hr); + return AGS_DX_FAILURE; + } + if (context->version < AMD_AGS_VERSION_5_2_0) + { + AGSDX11ReturnedParams_511 *r = &returned_params->agsDX11ReturnedParams511; + r->pDevice = device; + r->pImmediateContext = device_context; + r->pSwapChain = swapchain; + r->FeatureLevel = feature_level; + } + else if (context->version < AMD_AGS_VERSION_6_0_0) + { + AGSDX11ReturnedParams_520 *r = &returned_params->agsDX11ReturnedParams520; + r->pDevice = device; + r->pImmediateContext = device_context; + r->pSwapChain = swapchain; + r->FeatureLevel = feature_level; + } + else + { + AGSDX11ReturnedParams_600 *r = &returned_params->agsDX11ReturnedParams600; + r->pDevice = device; + r->pImmediateContext = device_context; + r->pSwapChain = swapchain; + r->featureLevel = feature_level; + } + + return AGS_SUCCESS; +} + AGSReturnCode WINAPI agsDriverExtensionsDX12_CreateDevice(AGSContext *context, const AGSDX12DeviceCreationParams *creation_params, const AGSDX12ExtensionParams *extension_params, AGSDX12ReturnedParams *returned_params) @@ -684,3 +771,71 @@ BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, void *reserved) return TRUE; } + +#ifdef __x86_64__ +AGSReturnCode WINAPI agsDriverExtensionsDX11_SetDepthBounds(AGSContext* context, bool enabled, + float minDepth, float maxDepth ) +{ + static int once; + + if (!once++) + FIXME("context %p, enabled %#x, minDepth %f, maxDepth %f stub.\n", context, enabled, minDepth, maxDepth); + return AGS_EXTENSION_NOT_SUPPORTED; +} + +AGSReturnCode WINAPI agsDriverExtensionsDX11_SetDepthBounds_530(AGSContext* context, + ID3D11DeviceContext* dxContext, bool enabled, float minDepth, float maxDepth ) +{ + static int once; + + if (!once++) + FIXME("context %p, enabled %#x, minDepth %f, maxDepth %f stub.\n", context, enabled, minDepth, maxDepth); + return AGS_EXTENSION_NOT_SUPPORTED; +} + +__ASM_GLOBAL_FUNC( DX11_SetDepthBounds_impl, + "mov (%rcx),%eax\n\t" /* version */ + "cmp $3,%eax\n\t" + "jge 1f\n\t" + "jmp " __ASM_NAME("agsDriverExtensionsDX11_SetDepthBounds") "\n\t" + "1:\tjmp " __ASM_NAME("agsDriverExtensionsDX11_SetDepthBounds_530") ) + +AGSReturnCode WINAPI agsDriverExtensionsDX11_DestroyDevice_520(AGSContext *context, ID3D11Device* device, + unsigned int *device_ref, ID3D11DeviceContext *device_context, + unsigned int *context_ref) +{ + ULONG ref; + + TRACE("context %p, device %p, device_ref %p, device_context %p, context_ref %p.\n", + context, device, device_ref, device_context, context_ref); + + if (!device) + return AGS_SUCCESS; + + ref = ID3D11Device_Release(device); + if (device_ref) + *device_ref = ref; + + if (!device_context) + return AGS_SUCCESS; + + ref = ID3D11DeviceContext_Release(device_context); + if (context_ref) + *context_ref = ref; + return AGS_SUCCESS; +} + +AGSReturnCode WINAPI agsDriverExtensionsDX11_DestroyDevice_511(AGSContext *context, ID3D11Device *device, + unsigned int *references ) +{ + TRACE("context %p, device %p, references %p.\n", context, device, references); + + return agsDriverExtensionsDX11_DestroyDevice_520(context, device, references, NULL, NULL); +} +__ASM_GLOBAL_FUNC( agsDriverExtensionsDX11_DestroyDevice, + "mov (%rcx),%eax\n\t" /* version */ + "cmp $1,%eax\n\t" + "jge 1f\n\t" + "jmp " __ASM_NAME("agsDriverExtensionsDX11_DestroyDevice_511") "\n\t" + "1:\tjmp " __ASM_NAME("agsDriverExtensionsDX11_DestroyDevice_520") ) +#endif diff --git a/dlls/evr/presenter.c b/dlls/evr/presenter.c index fd02404b7af..507217ffdda 100644 --- a/dlls/evr/presenter.c +++ b/dlls/evr/presenter.c @@ -65,6 +65,7 @@ struct sample_queue unsigned int used; unsigned int front; unsigned int back; + IMFSample *last_presented; }; struct streaming_thread @@ -120,6 +121,7 @@ struct video_presenter struct { int presented; + LONGLONG sampletime; } frame_stats; CRITICAL_SECTION cs; @@ -507,6 +509,13 @@ static void video_presenter_sample_present(struct video_presenter *presenter, IM IDirect3DSwapChain9_Present(presenter->swapchain, NULL, NULL, NULL, NULL, 0); presenter->frame_stats.presented++; + EnterCriticalSection(&presenter->cs); + if (presenter->thread.queue.last_presented) + IMFSample_Release(presenter->thread.queue.last_presented); + presenter->thread.queue.last_presented = sample; + IMFSample_AddRef(presenter->thread.queue.last_presented); + LeaveCriticalSection(&presenter->cs); + IDirect3DDevice9_Release(device); IDirect3DSurface9_Release(backbuffer); IDirect3DSurface9_Release(surface); @@ -739,6 +748,8 @@ static HRESULT video_presenter_end_streaming(struct video_presenter *presenter) TRACE("Terminated streaming thread tid %#x.\n", presenter->thread.tid); + if (presenter->thread.queue.last_presented) + IMFSample_Release(presenter->thread.queue.last_presented); memset(&presenter->thread, 0, sizeof(presenter->thread)); video_presenter_set_allocator_callback(presenter, NULL); @@ -1461,9 +1472,88 @@ static HRESULT WINAPI video_presenter_control_RepaintVideo(IMFVideoDisplayContro static HRESULT WINAPI video_presenter_control_GetCurrentImage(IMFVideoDisplayControl *iface, BITMAPINFOHEADER *header, BYTE **dib, DWORD *dib_size, LONGLONG *timestamp) { - FIXME("%p, %p, %p, %p, %p.\n", iface, header, dib, dib_size, timestamp); + struct video_presenter *presenter = impl_from_IMFVideoDisplayControl(iface); + IDirect3DSurface9 *readback = NULL, *surface; + D3DSURFACE_DESC surface_desc; + D3DLOCKED_RECT mapped_rect; + IDirect3DDevice9 *device; + IMFSample *sample; + LONG stride; + HRESULT hr; - return E_NOTIMPL; + TRACE("%p, %p, %p, %p, %p.\n", iface, header, dib, dib_size, timestamp); + + EnterCriticalSection(&presenter->cs); + + sample = presenter->thread.queue.last_presented; + presenter->thread.queue.last_presented = NULL; + + if (!presenter->swapchain || !sample) + { + hr = MF_E_INVALIDREQUEST; + } + else if (SUCCEEDED(hr = video_presenter_get_sample_surface(sample, &surface))) + { + IDirect3DSwapChain9_GetDevice(presenter->swapchain, &device); + IDirect3DSurface9_GetDesc(surface, &surface_desc); + + if (surface_desc.Format != D3DFMT_X8R8G8B8) + { + FIXME("Unexpected surface format %d.\n", surface_desc.Format); + hr = E_FAIL; + } + + if (SUCCEEDED(hr)) + { + if (FAILED(hr = IDirect3DDevice9_CreateOffscreenPlainSurface(device, surface_desc.Width, + surface_desc.Height, D3DFMT_X8R8G8B8, D3DPOOL_SYSTEMMEM, &readback, NULL))) + { + WARN("Failed to create readback surface, hr %#x.\n", hr); + } + } + + if (SUCCEEDED(hr)) + hr = IDirect3DDevice9_GetRenderTargetData(device, surface, readback); + + if (SUCCEEDED(hr)) + { + MFGetStrideForBitmapInfoHeader(D3DFMT_X8R8G8B8, surface_desc.Width, &stride); + *dib_size = abs(stride) * surface_desc.Height; + if (!(*dib = CoTaskMemAlloc(*dib_size))) + hr = E_OUTOFMEMORY; + } + + if (SUCCEEDED(hr)) + { + if (SUCCEEDED(hr = IDirect3DSurface9_LockRect(readback, &mapped_rect, NULL, D3DLOCK_READONLY))) + { + memcpy(*dib, mapped_rect.pBits, *dib_size); + IDirect3DSurface9_UnlockRect(readback); + } + } + + memset(header, 0, sizeof(*header)); + header->biSize = sizeof(*header); + header->biWidth = surface_desc.Width; + header->biHeight = surface_desc.Height; + header->biPlanes = 1; + header->biBitCount = 32; + header->biSizeImage = *dib_size; + IMFSample_GetSampleTime(sample, timestamp); + + if (readback) + IDirect3DSurface9_Release(readback); + IDirect3DSurface9_Release(surface); + + IDirect3DDevice9_Release(device); + } + + if (sample) + IMFSample_Release(sample); + + LeaveCriticalSection(&presenter->cs); + + return hr; } static HRESULT WINAPI video_presenter_control_SetBorderColor(IMFVideoDisplayControl *iface, COLORREF color) diff --git a/dlls/hnetcfg/Makefile.in b/dlls/hnetcfg/Makefile.in index 0a4cb8dd334..0ff7c511eb4 100644 --- a/dlls/hnetcfg/Makefile.in +++ b/dlls/hnetcfg/Makefile.in @@ -1,6 +1,6 @@ MODULE = hnetcfg.dll IMPORTS = oleaut32 ole32 advapi32 mpr uuid - +DELAYIMPORTS = ws2_32 winhttp shcore xmllite EXTRADLLFLAGS = -Wb,--prefer-native C_SRCS = \ diff --git a/dlls/hnetcfg/apps.c b/dlls/hnetcfg/apps.c index b6447cf4d63..9f0b30cd28a 100644 --- a/dlls/hnetcfg/apps.c +++ b/dlls/hnetcfg/apps.c @@ -29,7 +29,6 @@ #include "natupnp.h" #include "wine/debug.h" -#include "wine/heap.h" #include "hnetcfg_private.h" WINE_DEFAULT_DEBUG_CHANNEL(hnetcfg); @@ -62,7 +61,7 @@ static ULONG WINAPI fw_app_Release( { TRACE("destroying %p\n", fw_app); SysFreeString( fw_app->filename ); - HeapFree( GetProcessHeap(), 0, fw_app ); + free( fw_app ); } return refs; } @@ -115,7 +114,9 @@ static REFIID tid_id[] = &IID_INetFwPolicy, &IID_INetFwPolicy2, &IID_INetFwProfile, - &IID_IUPnPNAT + &IID_IUPnPNAT, + &IID_IStaticPortMappingCollection, + &IID_IStaticPortMapping, }; HRESULT get_typeinfo( enum type_id tid, ITypeInfo **ret ) @@ -269,7 +270,7 @@ static HRESULT WINAPI fw_app_put_ProcessImageFileName( fw_app *This = impl_from_INetFwAuthorizedApplication( iface ); UNIVERSAL_NAME_INFOW *info; DWORD sz, longsz; - WCHAR *path; + WCHAR *path, *new_path; DWORD res; FIXME("%p, %s\n", This, debugstr_w(image)); @@ -281,7 +282,7 @@ static HRESULT WINAPI fw_app_put_ProcessImageFileName( res = WNetGetUniversalNameW(image, UNIVERSAL_NAME_INFO_LEVEL, NULL, &sz); if (res == WN_MORE_DATA) { - if (!(path = heap_alloc(sz))) + if (!(path = malloc(sz))) return E_OUTOFMEMORY; info = (UNIVERSAL_NAME_INFOW *)&path; @@ -291,29 +292,30 @@ static HRESULT WINAPI fw_app_put_ProcessImageFileName( SysFreeString(This->filename); This->filename = SysAllocString(info->lpUniversalName); } - heap_free(path); + free(path); return HRESULT_FROM_WIN32(res); } sz = GetFullPathNameW(image, 0, NULL, NULL); - if (!(path = heap_alloc(++sz * sizeof(WCHAR)))) + if (!(path = malloc(++sz * sizeof(WCHAR)))) return E_OUTOFMEMORY; GetFullPathNameW(image, sz, path, NULL); longsz = GetLongPathNameW(path, path, sz); if (longsz > sz) { - if (!(path = heap_realloc(path, longsz * sizeof(WCHAR)))) + if (!(new_path = realloc(path, longsz * sizeof(WCHAR)))) { - heap_free(path); + free(path); return E_OUTOFMEMORY; } + path = new_path; GetLongPathNameW(path, path, longsz); } SysFreeString( This->filename ); This->filename = SysAllocString(path); - heap_free(path); + free(path); return This->filename ? S_OK : E_OUTOFMEMORY; } @@ -432,7 +434,7 @@ HRESULT NetFwAuthorizedApplication_create( IUnknown *pUnkOuter, LPVOID *ppObj ) TRACE("(%p,%p)\n", pUnkOuter, ppObj); - fa = HeapAlloc( GetProcessHeap(), 0, sizeof(*fa) ); + fa = malloc( sizeof(*fa) ); if (!fa) return E_OUTOFMEMORY; fa->INetFwAuthorizedApplication_iface.lpVtbl = &fw_app_vtbl; @@ -470,7 +472,7 @@ static ULONG WINAPI fw_apps_Release( if (!refs) { TRACE("destroying %p\n", fw_apps); - HeapFree( GetProcessHeap(), 0, fw_apps ); + free( fw_apps ); } return refs; } @@ -645,7 +647,7 @@ HRESULT NetFwAuthorizedApplications_create( IUnknown *pUnkOuter, LPVOID *ppObj ) TRACE("(%p,%p)\n", pUnkOuter, ppObj); - fa = HeapAlloc( GetProcessHeap(), 0, sizeof(*fa) ); + fa = malloc( sizeof(*fa) ); if (!fa) return E_OUTOFMEMORY; fa->INetFwAuthorizedApplications_iface.lpVtbl = &fw_apps_vtbl; diff --git a/dlls/hnetcfg/hnetcfg_private.h b/dlls/hnetcfg/hnetcfg_private.h index be2d0f3c7c6..9e6c4ec9618 100644 --- a/dlls/hnetcfg/hnetcfg_private.h +++ b/dlls/hnetcfg/hnetcfg_private.h @@ -28,6 +28,8 @@ enum type_id INetFwProfile_tid, INetFwRules_tid, IUPnPNAT_tid, + IStaticPortMappingCollection_tid, + IStaticPortMapping_tid, last_tid }; diff --git a/dlls/hnetcfg/manager.c b/dlls/hnetcfg/manager.c index 2c0790a73b5..fda69729901 100644 --- a/dlls/hnetcfg/manager.c +++ b/dlls/hnetcfg/manager.c @@ -58,7 +58,7 @@ static ULONG WINAPI fw_manager_Release( if (!refs) { TRACE("destroying %p\n", fw_manager); - HeapFree( GetProcessHeap(), 0, fw_manager ); + free( fw_manager ); } return refs; } @@ -244,7 +244,7 @@ HRESULT NetFwMgr_create( IUnknown *pUnkOuter, LPVOID *ppObj ) TRACE("(%p,%p)\n", pUnkOuter, ppObj); - fm = HeapAlloc( GetProcessHeap(), 0, sizeof(*fm) ); + fm = malloc( sizeof(*fm) ); if (!fm) return E_OUTOFMEMORY; fm->INetFwMgr_iface.lpVtbl = &fw_manager_vtbl; diff --git a/dlls/hnetcfg/policy.c b/dlls/hnetcfg/policy.c index 1f5b0daa568..7bdc1563d90 100644 --- a/dlls/hnetcfg/policy.c +++ b/dlls/hnetcfg/policy.c @@ -105,7 +105,7 @@ static ULONG WINAPI netfw_rules_Release( if (!refs) { TRACE("destroying %p\n", This); - HeapFree( GetProcessHeap(), 0, This ); + free( This ); } return refs; } @@ -265,7 +265,7 @@ static HRESULT create_INetFwRules(INetFwRules **object) TRACE("(%p)\n", object); - rules = HeapAlloc( GetProcessHeap(), 0, sizeof(*rules) ); + rules = malloc( sizeof(*rules) ); if (!rules) return E_OUTOFMEMORY; rules->INetFwRules_iface.lpVtbl = &fw_rules_vtbl; @@ -292,7 +292,7 @@ static ULONG WINAPI fw_policy_Release( if (!refs) { TRACE("destroying %p\n", fw_policy); - HeapFree( GetProcessHeap(), 0, fw_policy ); + free( fw_policy ); } return refs; } @@ -435,7 +435,7 @@ HRESULT NetFwPolicy_create( IUnknown *pUnkOuter, LPVOID *ppObj ) TRACE("(%p,%p)\n", pUnkOuter, ppObj); - fp = HeapAlloc( GetProcessHeap(), 0, sizeof(*fp) ); + fp = malloc( sizeof(*fp) ); if (!fp) return E_OUTOFMEMORY; fp->INetFwPolicy_iface.lpVtbl = &fw_policy_vtbl; @@ -487,7 +487,7 @@ static ULONG WINAPI fwpolicy2_Release(INetFwPolicy2 *iface) { INetFwRules_Release(fw_policy->fw_policy2_rules); TRACE("destroying %p\n", fw_policy); - HeapFree( GetProcessHeap(), 0, fw_policy ); + free( fw_policy ); } return refs; } @@ -768,7 +768,7 @@ HRESULT NetFwPolicy2_create( IUnknown *outer, void **obj ) TRACE("(%p,%p)\n", outer, obj); - fp = HeapAlloc( GetProcessHeap(), 0, sizeof(*fp) ); + fp = malloc( sizeof(*fp) ); if (!fp) return E_OUTOFMEMORY; fp->INetFwPolicy2_iface.lpVtbl = &fw_policy2_vtbl; @@ -778,7 +778,7 @@ HRESULT NetFwPolicy2_create( IUnknown *outer, void **obj ) if (FAILED(create_INetFwRules(&fp->fw_policy2_rules))) { - HeapFree( GetProcessHeap(), 0, fp ); + free( fp ); return E_OUTOFMEMORY; } diff --git a/dlls/hnetcfg/port.c b/dlls/hnetcfg/port.c index fd4ac4977d3..47bfc16be79 100644 --- a/dlls/hnetcfg/port.c +++ b/dlls/hnetcfg/port.c @@ -24,16 +24,1571 @@ #include "windef.h" #include "winbase.h" #include "winuser.h" +#include "string.h" +#include "initguid.h" +#include "assert.h" +#include "winsock2.h" +#include "winhttp.h" +#include "shlwapi.h" +#include "xmllite.h" #include "ole2.h" #include "netfw.h" #include "natupnp.h" -#include "wine/heap.h" #include "wine/debug.h" #include "hnetcfg_private.h" WINE_DEFAULT_DEBUG_CHANNEL(hnetcfg); +struct port_mapping +{ + BSTR external_ip; + LONG external; + BSTR protocol; + LONG internal; + BSTR client; + VARIANT_BOOL enabled; + BSTR descr; +}; + +struct xml_value_desc +{ + const WCHAR *name; + BSTR value; +}; + +static struct +{ + LONG refs; + BOOL winsock_initialized; + WCHAR locationW[256]; + HINTERNET session, connection; + WCHAR desc_urlpath[128]; + WCHAR control_url[256]; + unsigned int version; + struct port_mapping *mappings; + unsigned int mapping_count; +} +upnp_gateway_connection; + +static SRWLOCK upnp_gateway_connection_lock = SRWLOCK_INIT; + +static void free_port_mapping( struct port_mapping *mapping ) +{ + SysFreeString( mapping->external_ip ); + SysFreeString( mapping->protocol ); + SysFreeString( mapping->client ); + SysFreeString( mapping->descr ); +} + +static void free_mappings(void) +{ + unsigned int i; + + for (i = 0; i < upnp_gateway_connection.mapping_count; ++i) + free_port_mapping( &upnp_gateway_connection.mappings[i] ); + free( upnp_gateway_connection.mappings ); + upnp_gateway_connection.mappings = NULL; + upnp_gateway_connection.mapping_count = 0; +} + +static BOOL copy_port_mapping( struct port_mapping *dst, const struct port_mapping *src ) +{ + memset( dst, 0, sizeof(*dst) ); + +#define COPY_BSTR_CHECK(name) if (src->name && !(dst->name = SysAllocString( src->name ))) \ + { \ + free_port_mapping( dst ); \ + return FALSE; \ + } + + COPY_BSTR_CHECK( external_ip ); + COPY_BSTR_CHECK( protocol ); + COPY_BSTR_CHECK( client ); + COPY_BSTR_CHECK( descr ); +#undef COPY_BSTR_CHECK + + dst->external = src->external; + dst->internal = src->internal; + dst->enabled = src->enabled; + return TRUE; +} + +static BOOL parse_search_response( char *response, WCHAR *locationW, unsigned int location_size ) +{ + char *saveptr = NULL, *tok, *tok2; + unsigned int status; + + tok = strtok_s( response, "\n", &saveptr ); + if (!tok) return FALSE; + + /* HTTP/1.1 200 OK */ + tok2 = strtok( tok, " " ); + if (!tok2) return FALSE; + tok2 = strtok( NULL, " " ); + if (!tok2) return FALSE; + status = atoi( tok2 ); + if (status != HTTP_STATUS_OK) + { + WARN( "status %u.\n", status ); + return FALSE; + } + while ((tok = strtok_s( NULL, "\n", &saveptr ))) + { + tok2 = strtok( tok, " " ); + if (!tok2) continue; + if (!stricmp( tok2, "LOCATION:" )) + { + tok2 = strtok( NULL, " \r" ); + if (!tok2) + { + WARN( "Error parsing location.\n" ); + return FALSE; + } + return !!MultiByteToWideChar( CP_UTF8, 0, tok2, -1, locationW, location_size / 2 ); + } + } + return FALSE; +} + +static BOOL parse_desc_xml( const char *desc_xml ) +{ + static const WCHAR urn_wanipconnection[] = L"urn:schemas-upnp-org:service:WANIPConnection:"; + WCHAR control_url[ARRAY_SIZE(upnp_gateway_connection.control_url)]; + BOOL service_type_matches, control_url_found, found = FALSE; + unsigned int version = 0; + XmlNodeType node_type; + IXmlReader *reader; + const WCHAR *value; + BOOL ret = FALSE; + IStream *stream; + HRESULT hr; + + if (!(stream = SHCreateMemStream( (BYTE *)desc_xml, strlen( desc_xml ) + 1 ))) return FALSE; + if (FAILED(hr = CreateXmlReader( &IID_IXmlReader, (void **)&reader, NULL ))) + { + IStream_Release( stream ); + return FALSE; + } + if (FAILED(hr = IXmlReader_SetInput( reader, (IUnknown*)stream ))) goto done; + + while (SUCCEEDED(IXmlReader_Read( reader, &node_type )) && node_type != XmlNodeType_None) + { + if (node_type != XmlNodeType_Element) continue; + + if (FAILED(IXmlReader_GetLocalName( reader, &value, NULL ))) goto done; + if (wcsicmp( value, L"service" )) continue; + control_url_found = service_type_matches = FALSE; + while (SUCCEEDED(IXmlReader_Read( reader, &node_type ))) + { + if (node_type != XmlNodeType_Element && node_type != XmlNodeType_EndElement) continue; + if (FAILED(IXmlReader_GetLocalName( reader, &value, NULL ))) + { + WARN( "IXmlReader_GetLocalName failed.\n" ); + goto done; + } + if (node_type == XmlNodeType_EndElement) + { + if (!wcsicmp( value, L"service" )) break; + continue; + } + if (!wcsicmp( value, L"serviceType" )) + { + if (FAILED(IXmlReader_Read(reader, &node_type ))) goto done; + if (node_type != XmlNodeType_Text) goto done; + if (FAILED(IXmlReader_GetValue( reader, &value, NULL ))) goto done; + if (wcsnicmp( value, urn_wanipconnection, ARRAY_SIZE(urn_wanipconnection) - 1 )) break; + version = _wtoi( value + ARRAY_SIZE(urn_wanipconnection) - 1 ); + service_type_matches = version >= 1; + } + else if (!wcsicmp( value, L"controlURL" )) + { + if (FAILED(IXmlReader_Read(reader, &node_type ))) goto done; + if (node_type != XmlNodeType_Text) goto done; + if (FAILED(IXmlReader_GetValue( reader, &value, NULL ))) goto done; + if (wcslen( value ) + 1 > ARRAY_SIZE(control_url)) goto done; + wcscpy( control_url, value ); + control_url_found = TRUE; + } + } + if (service_type_matches && control_url_found) + { + if (found) + { + FIXME( "Found another WANIPConnection service, ignoring.\n" ); + continue; + } + found = TRUE; + wcscpy( upnp_gateway_connection.control_url, control_url ); + upnp_gateway_connection.version = version; + } + } + + ret = found; +done: + IXmlReader_Release( reader ); + IStream_Release( stream ); + return ret; +} + +static BOOL get_xml_elements( const char *desc_xml, struct xml_value_desc *values, unsigned int value_count ) +{ + XmlNodeType node_type; + IXmlReader *reader; + const WCHAR *value; + BOOL ret = FALSE; + IStream *stream; + unsigned int i; + HRESULT hr; + + for (i = 0; i < value_count; ++i) assert( !values[i].value ); + + if (!(stream = SHCreateMemStream( (BYTE *)desc_xml, strlen( desc_xml ) + 1 ))) return FALSE; + if (FAILED(hr = CreateXmlReader( &IID_IXmlReader, (void **)&reader, NULL ))) + { + IStream_Release( stream ); + return FALSE; + } + if (FAILED(hr = IXmlReader_SetInput( reader, (IUnknown*)stream ))) goto done; + + while (SUCCEEDED(IXmlReader_Read( reader, &node_type )) && node_type != XmlNodeType_None) + { + if (node_type != XmlNodeType_Element) continue; + + if (FAILED(IXmlReader_GetQualifiedName( reader, &value, NULL ))) goto done; + for (i = 0; i < value_count; ++i) + if (!wcsicmp( value, values[i].name )) break; + if (i == value_count) continue; + if (FAILED(IXmlReader_Read(reader, &node_type ))) goto done; + if (node_type != XmlNodeType_Text) + { + if (node_type == XmlNodeType_EndElement) value = L""; + else goto done; + } + else + { + if (FAILED(IXmlReader_GetValue( reader, &value, NULL ))) goto done; + } + if (values[i].value) + { + WARN( "Duplicate value %s.\n", debugstr_w(values[i].name) ); + goto done; + } + if (!(values[i].value = SysAllocString( value ))) goto done; + } + ret = TRUE; + +done: + if (!ret) + { + for (i = 0; i < value_count; ++i) + { + SysFreeString( values[i].value ); + values[i].value = NULL; + } + } + IXmlReader_Release( reader ); + IStream_Release( stream ); + return ret; +} + +static BOOL open_gateway_connection(void) +{ + static const int timeout = 3000; + + WCHAR hostname[64]; + URL_COMPONENTS url; + + memset( &url, 0, sizeof(url) ); + url.dwStructSize = sizeof(url); + url.lpszHostName = hostname; + url.dwHostNameLength = ARRAY_SIZE(hostname); + url.lpszUrlPath = upnp_gateway_connection.desc_urlpath; + url.dwUrlPathLength = ARRAY_SIZE(upnp_gateway_connection.desc_urlpath); + + if (!WinHttpCrackUrl( upnp_gateway_connection.locationW, 0, 0, &url )) return FALSE; + + upnp_gateway_connection.session = WinHttpOpen( L"hnetcfg", WINHTTP_ACCESS_TYPE_NO_PROXY, + WINHTTP_NO_PROXY_NAME, WINHTTP_NO_PROXY_BYPASS, 0 ); + if (!upnp_gateway_connection.session) return FALSE; + if (!WinHttpSetTimeouts( upnp_gateway_connection.session, timeout, timeout, timeout, timeout )) + return FALSE; + + TRACE( "hostname %s, urlpath %s, port %u.\n", + debugstr_w(hostname), debugstr_w(upnp_gateway_connection.desc_urlpath), url.nPort ); + upnp_gateway_connection.connection = WinHttpConnect ( upnp_gateway_connection.session, hostname, url.nPort, 0 ); + if (!upnp_gateway_connection.connection) + { + WARN( "WinHttpConnect error %u.\n", GetLastError() ); + return FALSE; + } + return TRUE; +} + +static BOOL get_control_url(void) +{ + static const WCHAR *accept_types[] = + { + L"text/xml", + NULL + }; + unsigned int desc_xml_size, offset; + DWORD size, status = 0; + HINTERNET request; + char *desc_xml; + BOOL ret; + + request = WinHttpOpenRequest( upnp_gateway_connection.connection, NULL, upnp_gateway_connection.desc_urlpath, NULL, + WINHTTP_NO_REFERER, accept_types, 0 ); + if (!request) return FALSE; + + if (!WinHttpSendRequest( request, WINHTTP_NO_ADDITIONAL_HEADERS, 0, NULL, 0, 0, 0 )) + { + WARN( "Error sending request %u.\n", GetLastError() ); + WinHttpCloseHandle( request ); + return FALSE; + } + if (!WinHttpReceiveResponse(request, NULL)) + { + WARN( "Error receiving response %u.\n", GetLastError() ); + WinHttpCloseHandle( request ); + return FALSE; + } + size = sizeof(status); + if (!WinHttpQueryHeaders( request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, + NULL, &status, &size, NULL) || status != HTTP_STATUS_OK ) + { + WARN( "Error response from server, error %u, http status %u.\n", GetLastError(), status ); + WinHttpCloseHandle( request ); + return FALSE; + } + desc_xml_size = 1024; + desc_xml = malloc( desc_xml_size ); + offset = 0; + while (WinHttpReadData( request, desc_xml + offset, desc_xml_size - offset - 1, &size ) && size) + { + offset += size; + if (offset + 1 == desc_xml_size) + { + char *new; + + desc_xml_size *= 2; + if (!(new = realloc( desc_xml, desc_xml_size ))) + { + ERR( "No memory.\n" ); + break; + } + desc_xml = new; + } + } + desc_xml[offset] = 0; + WinHttpCloseHandle( request ); + ret = parse_desc_xml( desc_xml ); + free( desc_xml ); + return ret; +} + +static void gateway_connection_cleanup(void) +{ + TRACE( ".\n" ); + free_mappings(); + WinHttpCloseHandle( upnp_gateway_connection.connection ); + WinHttpCloseHandle( upnp_gateway_connection.session ); + if (upnp_gateway_connection.winsock_initialized) WSACleanup(); + memset( &upnp_gateway_connection, 0, sizeof(upnp_gateway_connection) ); +} + +static BOOL request_service( const WCHAR *function, const struct xml_value_desc *request_param, + unsigned int request_param_count, struct xml_value_desc *result, + unsigned int result_count, DWORD *http_status, BSTR *server_error_code_str ) +{ + static const char request_template_header[] = + "\r\n" + "\r\n" + " \r\n" + " \r\n"; + static const char request_template_footer[] = + " \r\n" + " \r\n" + "\r\n"; + + unsigned int request_data_size, request_len, offset, i, reply_buffer_size; + char *request_data, *reply_buffer = NULL, *ptr; + struct xml_value_desc error_value_desc; + WCHAR request_headers[1024]; + HINTERNET request = NULL; + BOOL ret = FALSE; + DWORD size; + + *server_error_code_str = NULL; + request_data_size = strlen(request_template_header) + strlen(request_template_footer) + 2 * wcslen( function ) + + 9 /* version + zero terminator */; + for (i = 0; i < request_param_count; ++i) + { + request_data_size += 13 + 2 * wcslen( request_param[i].name ) + wcslen( request_param[i].value ); + } + if (!(request_data = malloc( request_data_size ))) return FALSE; + + request = WinHttpOpenRequest( upnp_gateway_connection.connection, L"POST", upnp_gateway_connection.control_url, + NULL, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, 0 ); + if (!request) goto done; + + ptr = request_data; + snprintf( ptr, request_data_size, request_template_header, function, upnp_gateway_connection.version ); + offset = strlen( ptr ); + ptr += offset; + request_data_size -= offset; + for (i = 0; i < request_param_count; ++i) + { + snprintf( ptr, request_data_size, " <%S>%S\r\n", + request_param[i].name, request_param[i].value, request_param[i].name); + offset = strlen( ptr ); + ptr += offset; + request_data_size -= offset; + } + snprintf( ptr, request_data_size, request_template_footer, function ); + + request_len = strlen( request_data ); + swprintf( request_headers, ARRAY_SIZE(request_headers), + L"SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:%u#%s\"\r\n" + L"Content-Type: text/xml", + upnp_gateway_connection.version, function ); + if (!WinHttpSendRequest( request, request_headers, -1, request_data, request_len, request_len, 0 )) + { + WARN( "Error sending request %u.\n", GetLastError() ); + goto done; + } + if (!WinHttpReceiveResponse(request, NULL)) + { + WARN( "Error receiving response %u.\n", GetLastError() ); + goto done; + } + size = sizeof(*http_status); + if (!WinHttpQueryHeaders( request, WINHTTP_QUERY_STATUS_CODE | WINHTTP_QUERY_FLAG_NUMBER, + NULL, http_status, &size, NULL) || *http_status != HTTP_STATUS_OK ) + { + if (*http_status != HTTP_STATUS_SERVER_ERROR) + { + ret = TRUE; + goto done; + } + } + + offset = 0; + reply_buffer_size = 1024; + if (!(reply_buffer = malloc( reply_buffer_size ))) goto done; + while ((ret = WinHttpReadData( request, reply_buffer + offset, reply_buffer_size - offset - 1, &size )) && size) + { + offset += size; + if (offset + 1 == reply_buffer_size) + { + char *new; + + reply_buffer_size *= 2; + if (!(new = realloc( reply_buffer, reply_buffer_size ))) goto done; + reply_buffer = new; + } + } + reply_buffer[offset] = 0; + + if (*http_status == HTTP_STATUS_OK) ret = get_xml_elements( reply_buffer, result, result_count ); + else + { + error_value_desc.name = L"errorCode"; + error_value_desc.value = NULL; + if ((ret = get_xml_elements( reply_buffer, &error_value_desc, 1 ))) + *server_error_code_str = error_value_desc.value; + } + +done: + free( reply_buffer ); + free( request_data ); + WinHttpCloseHandle( request ); + return ret; +} + +enum port_mapping_parameter +{ + PM_EXTERNAL_IP, + PM_EXTERNAL, + PM_PROTOCOL, + PM_INTERNAL, + PM_CLIENT, + PM_ENABLED, + PM_DESC, + PM_LEASE_DURATION, + PM_LAST, + PM_REMOVE_PORT_LAST = PM_INTERNAL, +}; + +static struct xml_value_desc port_mapping_template[] = +{ + { L"NewRemoteHost" }, + { L"NewExternalPort" }, + { L"NewProtocol" }, + { L"NewInternalPort" }, + { L"NewInternalClient" }, + { L"NewEnabled" }, + { L"NewPortMappingDescription" }, + { L"NewLeaseDuration" }, +}; + +static LONG long_from_bstr( BSTR s ) +{ + if (!s) return 0; + return _wtoi( s ); +} + +static BSTR mapping_move_bstr( BSTR *s ) +{ + BSTR ret; + + if (*s) + { + ret = *s; + *s = NULL; + } + else if (!(ret = SysAllocString( L"" ))) + { + ERR( "No memory.\n" ); + } + return ret; +} + +static void update_mapping_list(void) +{ + struct xml_value_desc mapping_desc[ARRAY_SIZE(port_mapping_template)]; + struct xml_value_desc index_param; + struct port_mapping *new_mappings; + unsigned int i, index; + WCHAR index_str[9]; + BSTR error_str; + DWORD status; + BOOL ret; + + free_mappings(); + + index_param.name = L"NewPortMappingIndex"; + + index = 0; + while (1) + { + new_mappings = realloc( upnp_gateway_connection.mappings, (index + 1) * sizeof(*new_mappings) ); + if (!new_mappings) break; + upnp_gateway_connection.mappings = new_mappings; + + memcpy( mapping_desc, port_mapping_template, sizeof(mapping_desc) ); + swprintf( index_str, ARRAY_SIZE(index_str), L"%u", index ); + index_param.value = SysAllocString( index_str ); + ret = request_service( L"GetGenericPortMappingEntry", &index_param, 1, + mapping_desc, ARRAY_SIZE(mapping_desc), &status, &error_str ); + SysFreeString( index_param.value ); + if (!ret) break; + if (status != HTTP_STATUS_OK) + { + if (error_str) + { + if (long_from_bstr( error_str ) != 713) + WARN( "Server returned error %s.\n", debugstr_w(error_str) ); + SysFreeString( error_str ); + } + break; + } + new_mappings[index].external_ip = mapping_move_bstr( &mapping_desc[PM_EXTERNAL_IP].value ); + new_mappings[index].external = long_from_bstr( mapping_desc[PM_EXTERNAL].value ); + new_mappings[index].protocol = mapping_move_bstr( &mapping_desc[PM_PROTOCOL].value ); + new_mappings[index].internal = long_from_bstr( mapping_desc[PM_INTERNAL].value ); + new_mappings[index].client = mapping_move_bstr( &mapping_desc[PM_CLIENT].value ); + if (mapping_desc[PM_ENABLED].value && (!wcsicmp( mapping_desc[PM_ENABLED].value, L"true" ) + || long_from_bstr( mapping_desc[PM_ENABLED].value ))) + new_mappings[index].enabled = VARIANT_TRUE; + else + new_mappings[index].enabled = VARIANT_FALSE; + new_mappings[index].descr = mapping_move_bstr( &mapping_desc[PM_DESC].value ); + + TRACE( "%s %s %s:%u -> %s:%u, enabled %d.\n", debugstr_w(new_mappings[index].descr), + debugstr_w(new_mappings[index].protocol), debugstr_w(new_mappings[index].external_ip), + new_mappings[index].external, debugstr_w(new_mappings[index].client), + new_mappings[index].internal, new_mappings[index].enabled ); + + for (i = 0; i < ARRAY_SIZE(mapping_desc); ++i) + SysFreeString( mapping_desc[i].value ); + upnp_gateway_connection.mappings = new_mappings; + upnp_gateway_connection.mapping_count = ++index; + } +} + +static BOOL remove_port_mapping( LONG port, BSTR protocol ) +{ + struct xml_value_desc mapping_desc[PM_REMOVE_PORT_LAST]; + DWORD status = 0; + BSTR error_str; + WCHAR portW[6]; + BOOL ret; + + AcquireSRWLockExclusive( &upnp_gateway_connection_lock ); + memcpy( mapping_desc, port_mapping_template, sizeof(mapping_desc) ); + swprintf( portW, ARRAY_SIZE(portW), L"%u", port ); + mapping_desc[PM_EXTERNAL_IP].value = SysAllocString( L"" ); + mapping_desc[PM_EXTERNAL].value = SysAllocString( portW ); + mapping_desc[PM_PROTOCOL].value = protocol; + + ret = request_service( L"DeletePortMapping", mapping_desc, PM_REMOVE_PORT_LAST, + NULL, 0, &status, &error_str ); + if (ret && status != HTTP_STATUS_OK) + { + WARN( "status %u, server returned error %s.\n", status, debugstr_w(error_str) ); + SysFreeString( error_str ); + ret = FALSE; + } + else if (!ret) + { + WARN( "Request failed.\n" ); + } + update_mapping_list(); + ReleaseSRWLockExclusive( &upnp_gateway_connection_lock ); + + SysFreeString( mapping_desc[PM_EXTERNAL_IP].value ); + SysFreeString( mapping_desc[PM_EXTERNAL].value ); + return ret; +} + +static BOOL add_port_mapping( LONG external, BSTR protocol, LONG internal, BSTR client, + VARIANT_BOOL enabled, BSTR description ) +{ + struct xml_value_desc mapping_desc[PM_LAST]; + WCHAR externalW[6], internalW[6]; + DWORD status = 0; + BSTR error_str; + BOOL ret; + + AcquireSRWLockExclusive( &upnp_gateway_connection_lock ); + memcpy( mapping_desc, port_mapping_template, sizeof(mapping_desc) ); + swprintf( externalW, ARRAY_SIZE(externalW), L"%u", external ); + swprintf( internalW, ARRAY_SIZE(internalW), L"%u", internal ); + mapping_desc[PM_EXTERNAL_IP].value = SysAllocString( L"" ); + mapping_desc[PM_EXTERNAL].value = SysAllocString( externalW ); + mapping_desc[PM_PROTOCOL].value = protocol; + mapping_desc[PM_INTERNAL].value = SysAllocString( internalW ); + mapping_desc[PM_CLIENT].value = client; + mapping_desc[PM_ENABLED].value = SysAllocString( enabled ? L"1" : L"0" ); + mapping_desc[PM_DESC].value = description; + mapping_desc[PM_LEASE_DURATION].value = SysAllocString( L"0" ); + + ret = request_service( L"AddPortMapping", mapping_desc, PM_LAST, + NULL, 0, &status, &error_str ); + if (ret && status != HTTP_STATUS_OK) + { + WARN( "status %u, server returned error %s.\n", status, debugstr_w(error_str) ); + SysFreeString( error_str ); + ret = FALSE; + } + else if (!ret) + { + WARN( "Request failed.\n" ); + } + update_mapping_list(); + ReleaseSRWLockExclusive( &upnp_gateway_connection_lock ); + + SysFreeString( mapping_desc[PM_EXTERNAL_IP].value ); + SysFreeString( mapping_desc[PM_EXTERNAL].value ); + SysFreeString( mapping_desc[PM_INTERNAL].value ); + SysFreeString( mapping_desc[PM_ENABLED].value ); + SysFreeString( mapping_desc[PM_LEASE_DURATION].value ); + return ret; +} + +static BOOL init_gateway_connection(void) +{ + static const char upnp_search_request[] = + "M-SEARCH * HTTP/1.1\r\n" + "HOST:239.255.255.250:1900\r\n" + "ST:upnp:rootdevice\r\n" + "MX:2\r\n" + "MAN:\"ssdp:discover\"\r\n" + "\r\n"; + + const DWORD timeout = 1000; + int len, address_len; + char buffer[2048]; + SOCKADDR_IN addr; + WSADATA wsa_data; + unsigned int i; + SOCKET s; + + upnp_gateway_connection.winsock_initialized = WSAStartup( MAKEWORD( 2, 2 ), &wsa_data ); + if ((s = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP )) == -1) + { + ERR( "Failed to create socket, error %u.\n", WSAGetLastError() ); + return FALSE; + } + if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char *)&timeout, sizeof(timeout)) == SOCKET_ERROR) + { + ERR( "setsockopt(SO_RCVTIME0) failed, error %u.\n", WSAGetLastError() ); + closesocket( s ); + return FALSE; + } + addr.sin_family = AF_INET; + addr.sin_port = htons( 1900 ); + addr.sin_addr.S_un.S_addr = inet_addr( "239.255.255.250" ); + if (sendto( s, upnp_search_request, strlen( upnp_search_request ), 0, (SOCKADDR *)&addr, sizeof(addr) ) + == SOCKET_ERROR) + { + ERR( "sendto failed, error %u\n", WSAGetLastError() ); + closesocket( s ); + return FALSE; + } + /* Windows has a dedicated SSDP discovery service which maintains gateway device info and does + * not usually delay in get_StaticPortMappingCollection(). Although it may still delay depending + * on network connection state and always delays in IUPnPNAT_get_NATEventManager(). */ + FIXME( "Waiting for reply from router.\n" ); + for (i = 0; i < 2; ++i) + { + address_len = sizeof(addr); + len = recvfrom( s, buffer, sizeof(buffer) - 1, 0, (SOCKADDR *)&addr, &address_len ); + if (len == -1) + { + if (WSAGetLastError() != WSAETIMEDOUT) + { + WARN( "recvfrom error %u.\n", WSAGetLastError() ); + closesocket( s ); + return FALSE; + } + } + else break; + } + closesocket( s ); + if (i == 2) + { + TRACE( "No reply from router.\n" ); + return FALSE; + } + TRACE( "Received reply from gateway, len %d.\n", len ); + buffer[len] = 0; + if (!parse_search_response( buffer, upnp_gateway_connection.locationW, sizeof(upnp_gateway_connection.locationW) )) + { + WARN( "Error parsing response.\n" ); + return FALSE; + } + TRACE( "Gateway description location %s.\n", debugstr_w(upnp_gateway_connection.locationW) ); + if (!open_gateway_connection()) + { + WARN( "Error opening gateway connection.\n" ); + return FALSE; + } + TRACE( "Opened gateway connection.\n" ); + if (!get_control_url()) + { + WARN( "Could not get_control URL.\n" ); + gateway_connection_cleanup(); + return FALSE; + } + TRACE( "control_url %s, version %u.\n", debugstr_w(upnp_gateway_connection.control_url), + upnp_gateway_connection.version ); + + update_mapping_list(); + return TRUE; +} + +static BOOL grab_gateway_connection(void) +{ + AcquireSRWLockExclusive( &upnp_gateway_connection_lock ); + if (!upnp_gateway_connection.refs && !init_gateway_connection()) + { + gateway_connection_cleanup(); + ReleaseSRWLockExclusive( &upnp_gateway_connection_lock ); + return FALSE; + } + ++upnp_gateway_connection.refs; + ReleaseSRWLockExclusive( &upnp_gateway_connection_lock ); + return TRUE; +} + +static void release_gateway_connection(void) +{ + AcquireSRWLockExclusive( &upnp_gateway_connection_lock ); + assert( upnp_gateway_connection.refs ); + if (!--upnp_gateway_connection.refs) + gateway_connection_cleanup(); + ReleaseSRWLockExclusive( &upnp_gateway_connection_lock ); +} + +static BOOL find_port_mapping( LONG port, BSTR protocol, struct port_mapping *ret ) +{ + unsigned int i; + BOOL found; + + AcquireSRWLockExclusive( &upnp_gateway_connection_lock ); + for (i = 0; i < upnp_gateway_connection.mapping_count; ++i) + { + if (upnp_gateway_connection.mappings[i].external == port + && !wcscmp( upnp_gateway_connection.mappings[i].protocol, protocol )) + break; + } + found = i < upnp_gateway_connection.mapping_count; + if (found) copy_port_mapping( ret, &upnp_gateway_connection.mappings[i] ); + ReleaseSRWLockExclusive( &upnp_gateway_connection_lock ); + return found; +} + +static unsigned int get_port_mapping_range( unsigned int index, unsigned int count, struct port_mapping *ret ) +{ + unsigned int i; + + AcquireSRWLockExclusive( &upnp_gateway_connection_lock ); + for (i = 0; i < count && index + i < upnp_gateway_connection.mapping_count; ++i) + if (!copy_port_mapping( &ret[i], &upnp_gateway_connection.mappings[index + i] )) + { + ERR( "No memory.\n" ); + break; + } + ReleaseSRWLockExclusive( &upnp_gateway_connection_lock ); + + return i; +} + +static unsigned int get_port_mapping_count(void) +{ + unsigned int ret; + + AcquireSRWLockExclusive( &upnp_gateway_connection_lock ); + ret = upnp_gateway_connection.mapping_count; + ReleaseSRWLockExclusive( &upnp_gateway_connection_lock ); + return ret; +} + +static BOOL is_valid_protocol( BSTR protocol ) +{ + if (!protocol) return FALSE; + return !wcscmp( protocol, L"UDP" ) || !wcscmp( protocol, L"TCP" ); +} + +struct static_port_mapping +{ + IStaticPortMapping IStaticPortMapping_iface; + LONG refs; + struct port_mapping data; +}; + +static inline struct static_port_mapping *impl_from_IStaticPortMapping( IStaticPortMapping *iface ) +{ + return CONTAINING_RECORD(iface, struct static_port_mapping, IStaticPortMapping_iface); +} + +static ULONG WINAPI static_port_mapping_AddRef( + IStaticPortMapping *iface ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + return InterlockedIncrement( &mapping->refs ); +} + +static ULONG WINAPI static_port_mapping_Release( + IStaticPortMapping *iface ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + LONG refs = InterlockedDecrement( &mapping->refs ); + if (!refs) + { + TRACE("destroying %p\n", mapping); + free_port_mapping( &mapping->data ); + free( mapping ); + } + return refs; +} + +static HRESULT WINAPI static_port_mapping_QueryInterface( + IStaticPortMapping *iface, + REFIID riid, + void **ppvObject ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + + TRACE("%p %s %p\n", mapping, debugstr_guid( riid ), ppvObject ); + + if ( IsEqualGUID( riid, &IID_IStaticPortMapping ) || + IsEqualGUID( riid, &IID_IDispatch ) || + IsEqualGUID( riid, &IID_IUnknown ) ) + { + *ppvObject = iface; + } + else + { + FIXME("interface %s not implemented\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + IStaticPortMapping_AddRef( iface ); + return S_OK; +} + +static HRESULT WINAPI static_port_mapping_GetTypeInfoCount( + IStaticPortMapping *iface, + UINT *pctinfo ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + + TRACE("%p %p\n", mapping, pctinfo); + *pctinfo = 1; + return S_OK; +} + +static HRESULT WINAPI static_port_mapping_GetTypeInfo( + IStaticPortMapping *iface, + UINT iTInfo, + LCID lcid, + ITypeInfo **ppTInfo ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + + TRACE("%p %u %u %p\n", mapping, iTInfo, lcid, ppTInfo); + return get_typeinfo( IStaticPortMapping_tid, ppTInfo ); +} + +static HRESULT WINAPI static_port_mapping_GetIDsOfNames( + IStaticPortMapping *iface, + REFIID riid, + LPOLESTR *rgszNames, + UINT cNames, + LCID lcid, + DISPID *rgDispId ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("%p %s %p %u %u %p\n", mapping, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); + + hr = get_typeinfo( IStaticPortMapping_tid, &typeinfo ); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_GetIDsOfNames( typeinfo, rgszNames, cNames, rgDispId ); + ITypeInfo_Release( typeinfo ); + } + return hr; +} + +static HRESULT WINAPI static_port_mapping_Invoke( + IStaticPortMapping *iface, + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("%p %d %s %d %d %p %p %p %p\n", mapping, dispIdMember, debugstr_guid(riid), + lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + + hr = get_typeinfo( IStaticPortMapping_tid, &typeinfo ); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_Invoke( typeinfo, &mapping->IStaticPortMapping_iface, dispIdMember, + wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr ); + ITypeInfo_Release( typeinfo ); + } + return hr; +} + +static HRESULT WINAPI static_port_mapping_get_ExternalIPAddress( + IStaticPortMapping *iface, + BSTR *value ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (!value) return E_POINTER; + *value = SysAllocString( mapping->data.external_ip ); + if (mapping->data.external_ip && !*value) return E_OUTOFMEMORY; + return S_OK; +} + +static HRESULT WINAPI static_port_mapping_get_ExternalPort( + IStaticPortMapping *iface, + LONG *value ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (!value) return E_POINTER; + *value = mapping->data.external; + return S_OK; +} + +static HRESULT WINAPI static_port_mapping_get_InternalPort( + IStaticPortMapping *iface, + LONG *value ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (!value) return E_POINTER; + *value = mapping->data.internal; + return S_OK; +} + +static HRESULT WINAPI static_port_mapping_get_Protocol( + IStaticPortMapping *iface, + BSTR *value ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (!value) return E_POINTER; + *value = SysAllocString( mapping->data.protocol ); + if (mapping->data.protocol && !*value) return E_OUTOFMEMORY; + return S_OK; +} + +static HRESULT WINAPI static_port_mapping_get_InternalClient( + IStaticPortMapping *iface, + BSTR *value ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (!value) return E_POINTER; + *value = SysAllocString( mapping->data.client ); + if (mapping->data.client && !*value) return E_OUTOFMEMORY; + return S_OK; +} + +static HRESULT WINAPI static_port_mapping_get_Enabled( + IStaticPortMapping *iface, + VARIANT_BOOL *value ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (!value) return E_POINTER; + *value = mapping->data.enabled; + return S_OK; +} + +static HRESULT WINAPI static_port_mapping_get_Description( + IStaticPortMapping *iface, + BSTR *value ) +{ + struct static_port_mapping *mapping = impl_from_IStaticPortMapping( iface ); + + TRACE( "iface %p, value %p.\n", iface, value ); + + if (!value) return E_POINTER; + *value = SysAllocString( mapping->data.descr ); + if (mapping->data.descr && !*value) return E_OUTOFMEMORY; + return S_OK; +} + +static HRESULT WINAPI static_port_mapping_EditInternalClient( + IStaticPortMapping *iface, + BSTR value ) +{ + FIXME( "iface %p, value %s stub.\n", iface, debugstr_w(value) ); + + return E_NOTIMPL; +} + +static HRESULT WINAPI static_port_mapping_Enable( + IStaticPortMapping *iface, + VARIANT_BOOL value ) +{ + FIXME( "iface %p, value %d stub.\n", iface, value ); + + return E_NOTIMPL; +} + +static HRESULT WINAPI static_port_mapping_EditDescription( + IStaticPortMapping *iface, + BSTR value ) +{ + FIXME( "iface %p, value %s stub.\n", iface, debugstr_w(value) ); + + return E_NOTIMPL; +} + +static HRESULT WINAPI static_port_mapping_EditInternalPort( + IStaticPortMapping *iface, + LONG value ) +{ + FIXME( "iface %p, value %d stub.\n", iface, value ); + + return E_NOTIMPL; +} + +static const IStaticPortMappingVtbl static_port_mapping_vtbl = +{ + static_port_mapping_QueryInterface, + static_port_mapping_AddRef, + static_port_mapping_Release, + static_port_mapping_GetTypeInfoCount, + static_port_mapping_GetTypeInfo, + static_port_mapping_GetIDsOfNames, + static_port_mapping_Invoke, + static_port_mapping_get_ExternalIPAddress, + static_port_mapping_get_ExternalPort, + static_port_mapping_get_InternalPort, + static_port_mapping_get_Protocol, + static_port_mapping_get_InternalClient, + static_port_mapping_get_Enabled, + static_port_mapping_get_Description, + static_port_mapping_EditInternalClient, + static_port_mapping_Enable, + static_port_mapping_EditDescription, + static_port_mapping_EditInternalPort, +}; + +static HRESULT static_port_mapping_create( const struct port_mapping *mapping_data, IStaticPortMapping **ret ) +{ + struct static_port_mapping *mapping; + + if (!(mapping = calloc( 1, sizeof(*mapping) ))) return E_OUTOFMEMORY; + + mapping->refs = 1; + mapping->IStaticPortMapping_iface.lpVtbl = &static_port_mapping_vtbl; + mapping->data = *mapping_data; + *ret = &mapping->IStaticPortMapping_iface; + return S_OK; +} + +struct port_mapping_enum +{ + IEnumVARIANT IEnumVARIANT_iface; + LONG refs; + unsigned int index; +}; + +static inline struct port_mapping_enum *impl_from_IEnumVARIANT( IEnumVARIANT *iface ) +{ + return CONTAINING_RECORD(iface, struct port_mapping_enum, IEnumVARIANT_iface); +} + +static ULONG WINAPI port_mapping_enum_AddRef( + IEnumVARIANT *iface ) +{ + struct port_mapping_enum *mapping_enum = impl_from_IEnumVARIANT( iface ); + return InterlockedIncrement( &mapping_enum->refs ); +} + +static ULONG WINAPI port_mapping_enum_Release( + IEnumVARIANT *iface ) +{ + struct port_mapping_enum *mapping_enum = impl_from_IEnumVARIANT( iface ); + LONG refs = InterlockedDecrement( &mapping_enum->refs ); + if (!refs) + { + TRACE("destroying %p\n", mapping_enum); + free( mapping_enum ); + release_gateway_connection(); + } + return refs; +} + +static HRESULT WINAPI port_mapping_enum_QueryInterface( + IEnumVARIANT *iface, + REFIID riid, + void **ppvObject ) +{ + struct port_mapping_enum *mapping_enum = impl_from_IEnumVARIANT( iface ); + + TRACE("%p %s %p\n", mapping_enum, debugstr_guid( riid ), ppvObject ); + + if ( IsEqualGUID( riid, &IID_IEnumVARIANT ) || + IsEqualGUID( riid, &IID_IUnknown ) ) + { + *ppvObject = iface; + } + else + { + FIXME("interface %s not implemented\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + IEnumVARIANT_AddRef( iface ); + return S_OK; +} + +static HRESULT WINAPI port_mapping_enum_Next( IEnumVARIANT *iface, ULONG celt, VARIANT *var, ULONG *fetched ) +{ + struct port_mapping_enum *mapping_enum = impl_from_IEnumVARIANT( iface ); + struct port_mapping *data; + IStaticPortMapping *pm; + unsigned int i, count; + HRESULT ret; + + TRACE( "iface %p, celt %u, var %p, fetched %p.\n", iface, celt, var, fetched ); + + if (fetched) *fetched = 0; + if (!celt) return S_OK; + if (!var) return E_POINTER; + + if (!(data = calloc( 1, celt * sizeof(*data) ))) return E_OUTOFMEMORY; + count = get_port_mapping_range( mapping_enum->index, celt, data ); + TRACE( "count %u.\n", count ); + for (i = 0; i < count; ++i) + { + if (FAILED(static_port_mapping_create( &data[i], &pm ))) break; + + V_VT(&var[i]) = VT_DISPATCH; + V_DISPATCH(&var[i]) = (IDispatch *)pm; + } + mapping_enum->index += i; + if (fetched) *fetched = i; + ret = (i < celt) ? S_FALSE : S_OK; + for ( ; i < count; ++i) + { + free_port_mapping( &data[i] ); + VariantInit( &var[i] ); + } + for ( ; i < celt; ++i) + VariantInit( &var[i] ); + + free( data ); + return ret; +} + +static HRESULT WINAPI port_mapping_enum_Skip( IEnumVARIANT *iface, ULONG celt ) +{ + struct port_mapping_enum *mapping_enum = impl_from_IEnumVARIANT( iface ); + unsigned int count = get_port_mapping_count(); + + TRACE( "iface %p, celt %u.\n", iface, celt ); + + mapping_enum->index += celt; + return mapping_enum->index <= count ? S_OK : S_FALSE; +} + +static HRESULT WINAPI port_mapping_enum_Reset( IEnumVARIANT *iface ) +{ + struct port_mapping_enum *mapping_enum = impl_from_IEnumVARIANT( iface ); + + TRACE( "iface %p.\n", iface ); + + mapping_enum->index = 0; + return S_OK; +} + +static HRESULT create_port_mapping_enum( IUnknown **ret ); + +static HRESULT WINAPI port_mapping_enum_Clone( IEnumVARIANT *iface, IEnumVARIANT **ret ) +{ + struct port_mapping_enum *mapping_enum = impl_from_IEnumVARIANT( iface ); + HRESULT hr; + + TRACE( "iface %p, ret %p.\n", iface, ret ); + + if (!ret) return E_POINTER; + *ret = NULL; + if (FAILED(hr = create_port_mapping_enum( (IUnknown **)ret ))) return hr; + impl_from_IEnumVARIANT( *ret )->index = mapping_enum->index; + return S_OK; +} + +static const IEnumVARIANTVtbl port_mapping_enum_vtbl = +{ + port_mapping_enum_QueryInterface, + port_mapping_enum_AddRef, + port_mapping_enum_Release, + port_mapping_enum_Next, + port_mapping_enum_Skip, + port_mapping_enum_Reset, + port_mapping_enum_Clone, +}; + +static HRESULT create_port_mapping_enum( IUnknown **ret ) +{ + struct port_mapping_enum *mapping_enum; + + if (!(mapping_enum = calloc( 1, sizeof(*mapping_enum) ))) return E_OUTOFMEMORY; + + grab_gateway_connection(); + + mapping_enum->refs = 1; + mapping_enum->IEnumVARIANT_iface.lpVtbl = &port_mapping_enum_vtbl; + mapping_enum->index = 0; + *ret = (IUnknown *)&mapping_enum->IEnumVARIANT_iface; + return S_OK; +} + +struct static_port_mapping_collection +{ + IStaticPortMappingCollection IStaticPortMappingCollection_iface; + LONG refs; +}; + +static inline struct static_port_mapping_collection *impl_from_IStaticPortMappingCollection + ( IStaticPortMappingCollection *iface ) +{ + return CONTAINING_RECORD(iface, struct static_port_mapping_collection, IStaticPortMappingCollection_iface); +} + +static ULONG WINAPI static_ports_AddRef( + IStaticPortMappingCollection *iface ) +{ + struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface ); + return InterlockedIncrement( &ports->refs ); +} + +static ULONG WINAPI static_ports_Release( + IStaticPortMappingCollection *iface ) +{ + struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface ); + LONG refs = InterlockedDecrement( &ports->refs ); + if (!refs) + { + TRACE("destroying %p\n", ports); + release_gateway_connection(); + free( ports ); + } + return refs; +} + +static HRESULT WINAPI static_ports_QueryInterface( + IStaticPortMappingCollection *iface, + REFIID riid, + void **ppvObject ) +{ + struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface ); + + TRACE("%p %s %p\n", ports, debugstr_guid( riid ), ppvObject ); + + if ( IsEqualGUID( riid, &IID_IStaticPortMappingCollection ) || + IsEqualGUID( riid, &IID_IDispatch ) || + IsEqualGUID( riid, &IID_IUnknown ) ) + { + *ppvObject = iface; + } + else + { + FIXME("interface %s not implemented\n", debugstr_guid(riid)); + return E_NOINTERFACE; + } + IStaticPortMappingCollection_AddRef( iface ); + return S_OK; +} + +static HRESULT WINAPI static_ports_GetTypeInfoCount( + IStaticPortMappingCollection *iface, + UINT *pctinfo ) +{ + struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface ); + + TRACE("%p %p\n", ports, pctinfo); + *pctinfo = 1; + return S_OK; +} + +static HRESULT WINAPI static_ports_GetTypeInfo( + IStaticPortMappingCollection *iface, + UINT iTInfo, + LCID lcid, + ITypeInfo **ppTInfo ) +{ + struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface ); + + TRACE("%p %u %u %p\n", ports, iTInfo, lcid, ppTInfo); + return get_typeinfo( IStaticPortMappingCollection_tid, ppTInfo ); +} + +static HRESULT WINAPI static_ports_GetIDsOfNames( + IStaticPortMappingCollection *iface, + REFIID riid, + LPOLESTR *rgszNames, + UINT cNames, + LCID lcid, + DISPID *rgDispId ) +{ + struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface ); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("%p %s %p %u %u %p\n", ports, debugstr_guid(riid), rgszNames, cNames, lcid, rgDispId); + + hr = get_typeinfo( IStaticPortMappingCollection_tid, &typeinfo ); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_GetIDsOfNames( typeinfo, rgszNames, cNames, rgDispId ); + ITypeInfo_Release( typeinfo ); + } + return hr; +} + +static HRESULT WINAPI static_ports_Invoke( + IStaticPortMappingCollection *iface, + DISPID dispIdMember, + REFIID riid, + LCID lcid, + WORD wFlags, + DISPPARAMS *pDispParams, + VARIANT *pVarResult, + EXCEPINFO *pExcepInfo, + UINT *puArgErr ) +{ + struct static_port_mapping_collection *ports = impl_from_IStaticPortMappingCollection( iface ); + ITypeInfo *typeinfo; + HRESULT hr; + + TRACE("%p %d %s %d %d %p %p %p %p\n", ports, dispIdMember, debugstr_guid(riid), + lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + + hr = get_typeinfo( IStaticPortMappingCollection_tid, &typeinfo ); + if (SUCCEEDED(hr)) + { + hr = ITypeInfo_Invoke( typeinfo, &ports->IStaticPortMappingCollection_iface, dispIdMember, + wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr ); + ITypeInfo_Release( typeinfo ); + } + return hr; +} + +static HRESULT WINAPI static_ports__NewEnum( + IStaticPortMappingCollection *iface, + IUnknown **ret ) +{ + TRACE( "iface %p, ret %p.\n", iface, ret ); + + if (!ret) return E_POINTER; + + *ret = NULL; + return create_port_mapping_enum( ret ); +} + +static HRESULT WINAPI static_ports_get_Item( + IStaticPortMappingCollection *iface, + LONG port, + BSTR protocol, + IStaticPortMapping **mapping ) +{ + struct port_mapping mapping_data; + HRESULT ret; + + TRACE( "iface %p, port %d, protocol %s.\n", iface, port, debugstr_w(protocol) ); + + if (!mapping) return E_POINTER; + *mapping = NULL; + if (!is_valid_protocol( protocol )) return E_INVALIDARG; + if (port < 0 || port > 65535) return E_INVALIDARG; + + if (!find_port_mapping( port, protocol, &mapping_data )) return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + if (FAILED(ret = static_port_mapping_create( &mapping_data, mapping ))) free_port_mapping( &mapping_data ); + return ret; +} + +static HRESULT WINAPI static_ports_get_Count( + IStaticPortMappingCollection *iface, + LONG *count ) +{ + TRACE( "iface %p, count %p.\n", iface, count ); + + if (!count) return E_POINTER; + *count = get_port_mapping_count(); + return S_OK; +} + +static HRESULT WINAPI static_ports_Remove( + IStaticPortMappingCollection *iface, + LONG port, + BSTR protocol ) +{ + TRACE( "iface %p, port %d, protocol %s.\n", iface, port, debugstr_w(protocol) ); + + if (!is_valid_protocol( protocol )) return E_INVALIDARG; + if (port < 0 || port > 65535) return E_INVALIDARG; + + if (!remove_port_mapping( port, protocol )) return E_FAIL; + + return S_OK; +} + +static HRESULT WINAPI static_ports_Add( + IStaticPortMappingCollection *iface, + LONG external, + BSTR protocol, + LONG internal, + BSTR client, + VARIANT_BOOL enabled, + BSTR description, + IStaticPortMapping **mapping ) +{ + struct port_mapping mapping_data; + HRESULT ret; + + TRACE( "iface %p, external %d, protocol %s, internal %d, client %s, enabled %d, descritption %s, mapping %p.\n", + iface, external, debugstr_w(protocol), internal, debugstr_w(client), enabled, debugstr_w(description), + mapping ); + + if (!mapping) return E_POINTER; + *mapping = NULL; + + if (!is_valid_protocol( protocol )) return E_INVALIDARG; + if (external < 0 || external > 65535) return E_INVALIDARG; + if (internal < 0 || internal > 65535) return E_INVALIDARG; + if (!client || !description) return E_INVALIDARG; + + if (!add_port_mapping( external, protocol, internal, client, enabled, description )) return E_FAIL; + + mapping_data.external_ip = NULL; + mapping_data.external = external; + mapping_data.protocol = SysAllocString( protocol ); + mapping_data.internal = internal; + mapping_data.client = SysAllocString( client ); + mapping_data.enabled = enabled; + mapping_data.descr = SysAllocString( description ); + if (!mapping_data.protocol || !mapping_data.client || !mapping_data.descr) + { + free_port_mapping( &mapping_data ); + return E_OUTOFMEMORY; + } + if (FAILED(ret = static_port_mapping_create( &mapping_data, mapping ))) free_port_mapping( &mapping_data ); + return ret; +} + +static const IStaticPortMappingCollectionVtbl static_ports_vtbl = +{ + static_ports_QueryInterface, + static_ports_AddRef, + static_ports_Release, + static_ports_GetTypeInfoCount, + static_ports_GetTypeInfo, + static_ports_GetIDsOfNames, + static_ports_Invoke, + static_ports__NewEnum, + static_ports_get_Item, + static_ports_get_Count, + static_ports_Remove, + static_ports_Add, +}; + +static HRESULT static_port_mapping_collection_create(IStaticPortMappingCollection **object) +{ + struct static_port_mapping_collection *ports; + + if (!object) return E_POINTER; + if (!grab_gateway_connection()) + { + *object = NULL; + return S_OK; + } + if (!(ports = calloc( 1, sizeof(*ports) ))) + { + release_gateway_connection(); + return E_OUTOFMEMORY; + } + ports->refs = 1; + ports->IStaticPortMappingCollection_iface.lpVtbl = &static_ports_vtbl; + *object = &ports->IStaticPortMappingCollection_iface; + return S_OK; +} + typedef struct fw_port { INetFwOpenPort INetFwOpenPort_iface; @@ -64,7 +1619,7 @@ static ULONG WINAPI fw_port_Release( { TRACE("destroying %p\n", fw_port); SysFreeString( fw_port->name ); - HeapFree( GetProcessHeap(), 0, fw_port ); + free( fw_port ); } return refs; } @@ -363,7 +1918,7 @@ HRESULT NetFwOpenPort_create( IUnknown *pUnkOuter, LPVOID *ppObj ) TRACE("(%p,%p)\n", pUnkOuter, ppObj); - fp = HeapAlloc( GetProcessHeap(), 0, sizeof(*fp) ); + fp = malloc( sizeof(*fp) ); if (!fp) return E_OUTOFMEMORY; fp->INetFwOpenPort_iface.lpVtbl = &fw_port_vtbl; @@ -404,7 +1959,7 @@ static ULONG WINAPI fw_ports_Release( if (!refs) { TRACE("destroying %p\n", fw_ports); - HeapFree( GetProcessHeap(), 0, fw_ports ); + free( fw_ports ); } return refs; } @@ -592,7 +2147,7 @@ HRESULT NetFwOpenPorts_create( IUnknown *pUnkOuter, LPVOID *ppObj ) TRACE("(%p,%p)\n", pUnkOuter, ppObj); - fp = HeapAlloc( GetProcessHeap(), 0, sizeof(*fp) ); + fp = malloc( sizeof(*fp) ); if (!fp) return E_OUTOFMEMORY; fp->INetFwOpenPorts_iface.lpVtbl = &fw_ports_vtbl; @@ -653,7 +2208,7 @@ static ULONG WINAPI upnpnat_Release(IUPnPNAT *iface) LONG refs = InterlockedDecrement( &This->ref ); if (!refs) { - heap_free( This ); + free( This ); } return refs; } @@ -717,10 +2272,10 @@ static HRESULT WINAPI upnpnat_Invoke(IUPnPNAT *iface, DISPID dispIdMember, REFII static HRESULT WINAPI upnpnat_get_StaticPortMappingCollection(IUPnPNAT *iface, IStaticPortMappingCollection **collection) { upnpnat *This = impl_from_IUPnPNAT( iface ); - FIXME("%p, %p\n", This, collection); - if(collection) - *collection = NULL; - return S_OK; + + TRACE("%p, %p\n", This, collection); + + return static_port_mapping_collection_create( collection ); } static HRESULT WINAPI upnpnat_get_DynamicPortMappingCollection(IUPnPNAT *iface, IDynamicPortMappingCollection **collection) @@ -762,7 +2317,7 @@ HRESULT IUPnPNAT_create(IUnknown *outer, void **object) TRACE("(%p,%p)\n", outer, object); - nat = heap_alloc( sizeof(*nat) ); + nat = malloc( sizeof(*nat) ); if (!nat) return E_OUTOFMEMORY; nat->IUPnPNAT_iface.lpVtbl = &upnpnat_vtbl; diff --git a/dlls/hnetcfg/profile.c b/dlls/hnetcfg/profile.c index d0e9f48dab4..63f6d0cc7ed 100644 --- a/dlls/hnetcfg/profile.c +++ b/dlls/hnetcfg/profile.c @@ -58,7 +58,7 @@ static ULONG WINAPI fw_profile_Release( if (!refs) { TRACE("destroying %p\n", fw_profile); - HeapFree( GetProcessHeap(), 0, fw_profile ); + free( fw_profile ); } return refs; } @@ -334,7 +334,7 @@ HRESULT NetFwProfile_create( IUnknown *pUnkOuter, LPVOID *ppObj ) TRACE("(%p,%p)\n", pUnkOuter, ppObj); - fp = HeapAlloc( GetProcessHeap(), 0, sizeof(*fp) ); + fp = malloc( sizeof(*fp) ); if (!fp) return E_OUTOFMEMORY; fp->INetFwProfile_iface.lpVtbl = &fw_profile_vtbl; diff --git a/dlls/hnetcfg/service.c b/dlls/hnetcfg/service.c index 5bfeedaaedf..122d9797bab 100644 --- a/dlls/hnetcfg/service.c +++ b/dlls/hnetcfg/service.c @@ -58,7 +58,7 @@ static ULONG WINAPI fw_service_Release( if (!refs) { TRACE("destroying %p\n", fw_service); - HeapFree( GetProcessHeap(), 0, fw_service ); + free( fw_service ); } return refs; } @@ -290,7 +290,7 @@ static HRESULT NetFwService_create( IUnknown *pUnkOuter, LPVOID *ppObj ) TRACE("(%p,%p)\n", pUnkOuter, ppObj); - fp = HeapAlloc( GetProcessHeap(), 0, sizeof(*fp) ); + fp = malloc( sizeof(*fp) ); if (!fp) return E_OUTOFMEMORY; fp->INetFwService_iface.lpVtbl = &fw_service_vtbl; @@ -328,7 +328,7 @@ static ULONG WINAPI fw_services_Release( if (!refs) { TRACE("destroying %p\n", fw_services); - HeapFree( GetProcessHeap(), 0, fw_services ); + free( fw_services ); } return refs; } @@ -464,7 +464,7 @@ HRESULT NetFwServices_create( IUnknown *pUnkOuter, LPVOID *ppObj ) TRACE("(%p,%p)\n", pUnkOuter, ppObj); - fp = HeapAlloc( GetProcessHeap(), 0, sizeof(*fp) ); + fp = malloc( sizeof(*fp) ); if (!fp) return E_OUTOFMEMORY; fp->INetFwServices_iface.lpVtbl = &fw_services_vtbl; diff --git a/dlls/hnetcfg/tests/policy.c b/dlls/hnetcfg/tests/policy.c index e2606933079..3b614e6e05a 100644 --- a/dlls/hnetcfg/tests/policy.c +++ b/dlls/hnetcfg/tests/policy.c @@ -29,6 +29,12 @@ #include "netfw.h" #include "natupnp.h" +static ULONG get_refcount(IUnknown *unk) +{ + IUnknown_AddRef(unk); + return IUnknown_Release(unk); +} + static void test_policy2_rules(INetFwPolicy2 *policy2) { HRESULT hr; @@ -164,6 +170,120 @@ static void test_NetFwAuthorizedApplication(void) INetFwAuthorizedApplication_Release(app); } +static void test_static_port_mapping_collection( IStaticPortMappingCollection *ports ) +{ + LONG i, count, count2, expected_count, external_port; + IStaticPortMapping *pm, *pm2; + ULONG refcount, refcount2; + IEnumVARIANT *enum_ports; + IUnknown *unk; + ULONG fetched; + BSTR protocol; + VARIANT var; + HRESULT hr; + + refcount = get_refcount((IUnknown *)ports); + hr = IStaticPortMappingCollection_get__NewEnum(ports, &unk); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + hr = IUnknown_QueryInterface(unk, &IID_IEnumVARIANT, (void **)&enum_ports); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + IUnknown_Release( unk ); + + refcount2 = get_refcount((IUnknown *)ports); + ok(refcount2 == refcount, "Got unexpected refcount %u, refcount2 %u.\n", refcount, refcount2); + + hr = IEnumVARIANT_Reset(enum_ports); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + count = 0xdeadbeef; + hr = IStaticPortMappingCollection_get_Count(ports, &count); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + hr = IStaticPortMappingCollection_get_Item(ports, 12345, (BSTR)L"UDP", &pm); + if (SUCCEEDED(hr)) + { + expected_count = count; + IStaticPortMapping_Release(pm); + } + else + { + expected_count = count + 1; + } + + hr = IStaticPortMappingCollection_Add(ports, 12345, (BSTR)L"udp", 12345, (BSTR)L"1.2.3.4", + VARIANT_TRUE, (BSTR)L"wine_test", &pm); + ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); + hr = IStaticPortMappingCollection_Add(ports, 12345, (BSTR)L"UDP", 12345, (BSTR)L"1.2.3.4", + VARIANT_TRUE, (BSTR)L"wine_test", &pm); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + hr = IStaticPortMappingCollection_get_Count(ports, &count2); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(count2 == expected_count, "Got unexpected count2 %u, expected %u.\n", count2, expected_count); + + hr = IStaticPortMappingCollection_get_Item(ports, 12345, NULL, &pm); + ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); + + hr = IStaticPortMappingCollection_get_Item(ports, 12345, (BSTR)L"UDP", NULL); + ok(hr == E_POINTER, "Got unexpected hr %#x.\n", hr); + + hr = IStaticPortMappingCollection_get_Item(ports, 12345, (BSTR)L"udp", &pm); + ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); + + hr = IStaticPortMappingCollection_get_Item(ports, -1, (BSTR)L"UDP", &pm); + ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); + + hr = IStaticPortMappingCollection_get_Item(ports, 65536, (BSTR)L"UDP", &pm); + ok(hr == E_INVALIDARG, "Got unexpected hr %#x.\n", hr); + + hr = IStaticPortMappingCollection_get_Item(ports, 12346, (BSTR)L"UDP", &pm); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "Got unexpected hr %#x.\n", hr); + + hr = IEnumVARIANT_Reset(enum_ports); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + for (i = 0; i < count2; ++i) + { + VariantInit(&var); + + fetched = 0xdeadbeef; + hr = IEnumVARIANT_Next(enum_ports, 1, &var, &fetched); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(fetched == 1, "Got unexpected fetched %u.\n", fetched); + ok(V_VT(&var) == VT_DISPATCH, "Got unexpected variant type %u.\n", V_VT(&var)); + + hr = IDispatch_QueryInterface(V_DISPATCH(&var), &IID_IStaticPortMapping, (void **)&pm); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + hr = IStaticPortMapping_get_Protocol(pm, &protocol); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + external_port = 0xdeadbeef; + hr = IStaticPortMapping_get_ExternalPort(pm, &external_port); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + ok(!wcscmp(protocol, L"UDP") || !wcscmp(protocol, L"TCP"), "Got unexpected protocol %s.\n", + debugstr_w(protocol)); + hr = IStaticPortMappingCollection_get_Item(ports, external_port, protocol, &pm2); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + ok(pm2 != pm, "Got same interface.\n"); + + IStaticPortMapping_Release(pm); + IStaticPortMapping_Release(pm2); + + SysFreeString(protocol); + + VariantClear(&var); + } + hr = IEnumVARIANT_Next(enum_ports, 1, &var, &fetched); + ok(hr == S_FALSE, "Got unexpected hr %#x.\n", hr); + + hr = IStaticPortMappingCollection_Remove(ports, 12345, (BSTR)L"UDP"); + ok(hr == S_OK, "Got unexpected hr %#x.\n", hr); + + IEnumVARIANT_Release(enum_ports); +} + static void test_IUPnPNAT(void) { IUPnPNAT *nat; @@ -171,6 +291,7 @@ static void test_IUPnPNAT(void) IDynamicPortMappingCollection *dync_ports; INATEventManager *manager; IProvideClassInfo *provider; + ULONG refcount, refcount2; HRESULT hr; hr = CoCreateInstance(&CLSID_UPnPNAT, NULL, CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER, &IID_IUPnPNAT, (void**)&nat); @@ -179,11 +300,21 @@ static void test_IUPnPNAT(void) hr = IUPnPNAT_QueryInterface(nat, &IID_IProvideClassInfo, (void**)&provider); ok(hr == E_NOINTERFACE, "got: %08x\n", hr); + refcount = get_refcount((IUnknown *)nat); hr = IUPnPNAT_get_StaticPortMappingCollection(nat, &static_ports); + ok(hr == S_OK, "got: %08x\n", hr); if(hr == S_OK && static_ports) + { + refcount2 = get_refcount((IUnknown *)nat); + ok(refcount2 == refcount, "Got unexpected refcount %u, refcount2 %u.\n", refcount, refcount2); + test_static_port_mapping_collection( static_ports ); IStaticPortMappingCollection_Release(static_ports); - + } + else if (hr == S_OK) + { + skip( "UPNP gateway not found.\n" ); + } hr = IUPnPNAT_get_DynamicPortMappingCollection(nat, &dync_ports); ok(hr == S_OK || hr == E_NOTIMPL /* Windows 8.1 */, "got: %08x\n", hr); if(hr == S_OK && dync_ports) diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c index 22f9115011e..601164a82a8 100644 --- a/dlls/kernelbase/process.c +++ b/dlls/kernelbase/process.c @@ -489,18 +489,15 @@ BOOL WINAPI DECLSPEC_HOTPATCH CreateProcessInternalA( HANDLE token, const char * static int battleye_launcher_redirect_hack(const WCHAR *app_name, WCHAR *new_name, DWORD new_name_len, WCHAR **cmd_line) { - WCHAR full_path[MAX_PATH], config_path[MAX_PATH]; + static const WCHAR belauncherW[] = L"c:\\windows\\system32\\belauncher.exe"; + + WCHAR full_path[MAX_PATH]; WCHAR *p; DWORD size; void *block; DWORD *translation; char buf[100]; char *product_name; - int launcher_exe_len, game_exe_len, arg_len; - HANDLE launcher_cfg; - LARGE_INTEGER launcher_cfg_size; - char *configs, *config, *arch_32_exe = NULL, *arch_64_exe = NULL, *game_exe, *be_arg = NULL; - BOOL wow64; WCHAR *new_cmd_line; if (!GetLongPathNameW( app_name, full_path, MAX_PATH )) lstrcpynW( full_path, app_name, MAX_PATH ); @@ -541,96 +538,15 @@ static int battleye_launcher_redirect_hack(const WCHAR *app_name, WCHAR *new_nam HeapFree( GetProcessHeap(), 0, block ); - TRACE("Detected launch of a BattlEye Launcher, attempting to launch game executable instead.\n"); - - lstrcpynW(config_path, full_path, MAX_PATH); - - for (p = config_path + wcslen(config_path); p != config_path; --p) - if (*p == '\\') break; - - if (*p == '\\') - { - *p = 0; - launcher_exe_len = wcslen(p + 1); - } - else - launcher_exe_len = wcslen(full_path); - - lstrcatW(config_path, L"\\BattlEye\\BELauncher.ini"); - - launcher_cfg = CreateFileW(config_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); - if (launcher_cfg == INVALID_HANDLE_VALUE) - return 0; - - if(!GetFileSizeEx(launcher_cfg, &launcher_cfg_size) || launcher_cfg_size.u.HighPart) - { - CloseHandle(launcher_cfg); - return 0; - } - - configs = HeapAlloc( GetProcessHeap(), 0, launcher_cfg_size.u.LowPart); - - if (!ReadFile(launcher_cfg, configs, launcher_cfg_size.u.LowPart, &size, NULL) || size != launcher_cfg_size.u.LowPart) - { - CloseHandle(launcher_cfg); - HeapFree( GetProcessHeap(), 0, configs ); - return 0; - } - - CloseHandle(launcher_cfg); - - config = configs; - do - { - if (!strncmp(config, "32BitExe=", 9)) - arch_32_exe = config + 9; - - if (!strncmp(config, "64BitExe=", 9)) - arch_64_exe = config + 9; - - if (!strncmp(config, "BEArg=", 6)) - be_arg = config + 6; - } - while ((config = strchr(config, '\n')) && *(config++)); - - if (arch_64_exe && (sizeof(void *) == 8 || (IsWow64Process(GetCurrentProcess(), &wow64) && wow64))) - game_exe = arch_64_exe; - else if (arch_32_exe) - game_exe = arch_32_exe; - else - { - HeapFree( GetProcessHeap(), 0, configs ); - WARN("Failed to find game executable name from BattlEye config.\n"); - return 0; - } - - if (strchr(game_exe, '\r')) - *(strchr(game_exe, '\r')) = 0; - if (strchr(game_exe, '\n')) - *(strchr(game_exe, '\n')) = 0; - game_exe_len = MultiByteToWideChar(CP_ACP, 0, game_exe, -1, NULL, 0) - 1; - - if (be_arg) - { - if (strchr(be_arg, '\r')) - *(strchr(be_arg, '\r')) = 0; - if (strchr(be_arg, '\n')) - *(strchr(be_arg, '\n')) = 0; - arg_len = MultiByteToWideChar(CP_ACP, 0, be_arg, -1, NULL, 0) - 1; - } - - TRACE("Launching game executable %s for BattlEye.\n", game_exe); + TRACE("Detected launch of a BattlEye Launcher, redirecting to Proton version.\n"); - if ((wcslen(app_name) - launcher_exe_len) + game_exe_len + 1 > new_name_len) + if (new_name_len < wcslen(belauncherW) + 1) { - HeapFree( GetProcessHeap(), 0, configs ); WARN("Game executable path doesn't fit in buffer.\n"); return 0; } - wcscpy(new_name, app_name); - p = new_name + wcslen(new_name) - launcher_exe_len; - MultiByteToWideChar(CP_ACP, 0, game_exe, -1, p, game_exe_len + 1); + wcscpy(new_name, belauncherW); /* find and replace executable name in command line, and add BE argument */ p = *cmd_line; @@ -638,43 +554,23 @@ static int battleye_launcher_redirect_hack(const WCHAR *app_name, WCHAR *new_nam p++; if (!wcsncmp(p, app_name, wcslen(app_name))) - p += wcslen(app_name) - launcher_exe_len; - else - p = NULL; - - if (p || be_arg) { - size = wcslen(*cmd_line) + 1; - if (p) - size += game_exe_len - launcher_exe_len; - if (be_arg) - size += 1 /* space */ + arg_len; - size *= sizeof(WCHAR); + new_cmd_line = HeapAlloc( GetProcessHeap(), 0, ( wcslen(*cmd_line) + wcslen(belauncherW) + 1 - wcslen(app_name) ) * sizeof(WCHAR) ); - /* freed by parent function */ - new_cmd_line = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size ); + wcscpy(new_cmd_line, *cmd_line); + p = new_cmd_line; + if (p[0] == '\"') + p++; - if (p) - { - lstrcpynW(new_cmd_line, *cmd_line, p - *cmd_line); - MultiByteToWideChar(CP_ACP, 0, game_exe, -1, new_cmd_line + wcslen(new_cmd_line), game_exe_len + 1); - wcscat(new_cmd_line, p + launcher_exe_len); - } - else - { - wcscpy(new_cmd_line, *cmd_line); - } + memmove( p + wcslen(belauncherW), p + wcslen(app_name), (wcslen(p) - wcslen(belauncherW)) * sizeof(WCHAR) ); + memcpy( p, belauncherW, wcslen(belauncherW) * sizeof(WCHAR) ); - if (be_arg) - { - wcscat(new_cmd_line, L" "); - MultiByteToWideChar(CP_ACP, 0, be_arg, -1, new_cmd_line + wcslen(new_cmd_line), arg_len + 1); - } + TRACE("old command line %s.\n", debugstr_w(*cmd_line)); + TRACE("new command line %s.\n", debugstr_w(new_cmd_line)); *cmd_line = new_cmd_line; } - HeapFree( GetProcessHeap(), 0, configs ); return 1; } @@ -690,6 +586,7 @@ static const WCHAR *hack_append_command_line( const WCHAR *cmd ) {L"UplayWebCore.exe", L" --use-gl=swiftshader"}, {L"Paradox Launcher.exe", L" --use-gl=swiftshader --in-process-gpu"}, {L"UnrealCEFSubProcess.exe", L" --use-gl=swiftshader"}, + {L"SocialClubHelper.exe", L" --use-gl=swiftshader --in-process-gpu"}, }; unsigned int i; diff --git a/dlls/mf/session.c b/dlls/mf/session.c index c8ec1b5c33f..f2fe2a83ff6 100644 --- a/dlls/mf/session.c +++ b/dlls/mf/session.c @@ -1577,6 +1577,8 @@ static void session_set_topology(struct media_session *session, DWORD flags, IMF { hr = session_bind_output_nodes(topology); + IMFTopology_SetUINT32(topology, &MF_TOPOLOGY_ENUMERATE_SOURCE_TYPES, TRUE); + if (SUCCEEDED(hr)) hr = IMFTopoLoader_Load(session->topo_loader, topology, &resolved_topology, NULL /* FIXME? */); @@ -2321,6 +2323,8 @@ static enum object_state session_get_object_state_for_event(MediaEventType event { switch (event) { + case MESourceSeeked: + case MEStreamSeeked: case MESourceStarted: case MEStreamStarted: case MEStreamSinkStarted: @@ -2497,6 +2501,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn switch (event_type) { + case MESourceSeeked: case MESourceStarted: case MESourcePaused: case MESourceStopped: @@ -2511,6 +2516,7 @@ static void session_set_source_object_state(struct media_session *session, IUnkn } } break; + case MEStreamSeeked: case MEStreamStarted: case MEStreamPaused: case MEStreamStopped: @@ -3197,6 +3203,10 @@ static HRESULT WINAPI session_events_callback_Invoke(IMFAsyncCallback *iface, IM switch (event_type) { + case MESourceSeeked: + case MEStreamSeeked: + FIXME("Source/stream seeking, semi-stub!\n"); + /* fallthrough */ case MESourceStarted: case MESourcePaused: case MESourceStopped: diff --git a/dlls/mfplat/tests/mfplat.c b/dlls/mfplat/tests/mfplat.c index 207bd61b8dc..1b1c8e90da6 100644 --- a/dlls/mfplat/tests/mfplat.c +++ b/dlls/mfplat/tests/mfplat.c @@ -942,6 +942,44 @@ todo_wine video_stream = (IMFMediaStream *)var.punkVal; } + hr = IMFMediaSource_Pause(mediasource); + ok(hr == S_OK, "Failed to pause media source, hr %#x.\n", hr); + if (get_event((IMFMediaEventGenerator *)mediasource, MESourcePaused, &var)) + ok(var.vt == VT_EMPTY, "Unexpected value type.\n"); + + var.vt = VT_EMPTY; + hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var); + ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr); + + if (get_event((IMFMediaEventGenerator *)mediasource, MESourceStarted, &var)) + ok(var.vt == VT_EMPTY, "Unexpected value type.\n"); + + hr = IMFMediaSource_Pause(mediasource); + ok(hr == S_OK, "Failed to pause media source, hr %#x.\n", hr); + if (get_event((IMFMediaEventGenerator *)mediasource, MESourcePaused, &var)) + ok(var.vt == VT_EMPTY, "Unexpected value type.\n"); + + var.vt = VT_I8; + var.uhVal.QuadPart = 0; + hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var); + ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr); + + if (get_event((IMFMediaEventGenerator *)mediasource, MESourceSeeked, &var)) + ok(var.vt == VT_I8, "Unexpected value type.\n"); + + hr = IMFMediaSource_Stop(mediasource); + ok(hr == S_OK, "Failed to pause media source, hr %#x.\n", hr); + if (get_event((IMFMediaEventGenerator *)mediasource, MESourceStopped, &var)) + ok(var.vt == VT_EMPTY, "Unexpected value type.\n"); + + var.vt = VT_I8; + var.uhVal.QuadPart = 0; + hr = IMFMediaSource_Start(mediasource, descriptor, &GUID_NULL, &var); + ok(hr == S_OK, "Failed to start media source, hr %#x.\n", hr); + + if (get_event((IMFMediaEventGenerator *)mediasource, MESourceStarted, &var)) + ok(var.vt == VT_I8, "Unexpected value type.\n"); + sample_count = 10; for (i = 0; i < sample_count; ++i) diff --git a/dlls/mfreadwrite/reader.c b/dlls/mfreadwrite/reader.c index 0cc3b3c9c54..de6784ecac1 100644 --- a/dlls/mfreadwrite/reader.c +++ b/dlls/mfreadwrite/reader.c @@ -72,6 +72,7 @@ enum media_stream_flags STREAM_FLAG_SAMPLE_REQUESTED = 0x1, /* Protects from making multiple sample requests. */ STREAM_FLAG_SELECTED = 0x2, /* Mirrors descriptor, used to simplify tests when starting the source. */ STREAM_FLAG_PRESENTED = 0x4, /* Set if stream was selected last time Start() was called. */ + STREAM_FLAG_STOPPED = 0x8, /* Received MEStreamStopped */ }; struct stream_transform @@ -154,6 +155,7 @@ struct source_reader IMFAsyncCallback stream_events_callback; IMFAsyncCallback async_commands_callback; LONG refcount; + LONG public_refcount; IMFMediaSource *source; IMFPresentationDescriptor *descriptor; IMFSourceReaderCallback *async_callback; @@ -171,6 +173,7 @@ struct source_reader CRITICAL_SECTION cs; CONDITION_VARIABLE sample_event; CONDITION_VARIABLE state_event; + CONDITION_VARIABLE stop_event; }; static inline struct source_reader *impl_from_IMFSourceReader(IMFSourceReader *iface) @@ -203,6 +206,51 @@ static struct media_stream *impl_stream_from_IMFVideoSampleAllocatorNotify(IMFVi return CONTAINING_RECORD(iface, struct media_stream, notify_cb); } +static void source_reader_release_responses(struct source_reader *reader, struct media_stream *stream); + +static ULONG source_reader_addref(struct source_reader *reader) +{ + return InterlockedIncrement(&reader->refcount); +} + +static ULONG source_reader_release(struct source_reader *reader) +{ + ULONG refcount = InterlockedDecrement(&reader->refcount); + unsigned int i; + + if (!refcount) + { + if (reader->async_callback) + IMFSourceReaderCallback_Release(reader->async_callback); + if (reader->descriptor) + IMFPresentationDescriptor_Release(reader->descriptor); + if (reader->attributes) + IMFAttributes_Release(reader->attributes); + IMFMediaSource_Release(reader->source); + + for (i = 0; i < reader->stream_count; ++i) + { + struct media_stream *stream = &reader->streams[i]; + + if (stream->stream) + IMFMediaStream_Release(stream->stream); + if (stream->current) + IMFMediaType_Release(stream->current); + if (stream->decoder.transform) + IMFTransform_Release(stream->decoder.transform); + if (stream->allocator) + IMFVideoSampleAllocatorEx_Release(stream->allocator); + } + source_reader_release_responses(reader, NULL); + free(reader->streams); + MFUnlockWorkQueue(reader->queue); + DeleteCriticalSection(&reader->cs); + free(reader); + } + + return refcount; +} + static HRESULT WINAPI source_reader_async_command_QueryInterface(IUnknown *iface, REFIID riid, void **obj) { if (IsEqualIID(riid, &IID_IUnknown)) @@ -325,13 +373,13 @@ static HRESULT WINAPI source_reader_callback_QueryInterface(IMFAsyncCallback *if static ULONG WINAPI source_reader_source_events_callback_AddRef(IMFAsyncCallback *iface) { struct source_reader *reader = impl_from_source_callback_IMFAsyncCallback(iface); - return IMFSourceReader_AddRef(&reader->IMFSourceReader_iface); + return source_reader_addref(reader); } static ULONG WINAPI source_reader_source_events_callback_Release(IMFAsyncCallback *iface) { struct source_reader *reader = impl_from_source_callback_IMFAsyncCallback(iface); - return IMFSourceReader_Release(&reader->IMFSourceReader_iface); + return source_reader_release(reader); } static HRESULT WINAPI source_reader_callback_GetParameters(IMFAsyncCallback *iface, @@ -539,6 +587,8 @@ static HRESULT source_reader_source_state_handler(struct source_reader *reader, LeaveCriticalSection(&reader->cs); WakeAllConditionVariable(&reader->state_event); + if (event_type == MESourceStopped) + WakeAllConditionVariable(&reader->stop_event); return S_OK; } @@ -594,7 +644,8 @@ static HRESULT WINAPI source_reader_source_events_callback_Invoke(IMFAsyncCallba IMFMediaEvent_Release(event); - IMFMediaSource_BeginGetEvent(source, iface, (IUnknown *)source); + if (event_type != MESourceStopped) + IMFMediaSource_BeginGetEvent(source, iface, (IUnknown *)source); return S_OK; } @@ -611,13 +662,13 @@ static const IMFAsyncCallbackVtbl source_events_callback_vtbl = static ULONG WINAPI source_reader_stream_events_callback_AddRef(IMFAsyncCallback *iface) { struct source_reader *reader = impl_from_stream_callback_IMFAsyncCallback(iface); - return IMFSourceReader_AddRef(&reader->IMFSourceReader_iface); + return source_reader_addref(reader); } static ULONG WINAPI source_reader_stream_events_callback_Release(IMFAsyncCallback *iface) { struct source_reader *reader = impl_from_stream_callback_IMFAsyncCallback(iface); - return IMFSourceReader_Release(&reader->IMFSourceReader_iface); + return source_reader_release(reader); } static HRESULT source_reader_pull_stream_samples(struct source_reader *reader, struct media_stream *stream) @@ -810,6 +861,9 @@ static HRESULT source_reader_media_stream_state_handler(struct source_reader *re case MEStreamStarted: stream->state = STREAM_STATE_READY; break; + case MEStreamStopped: + stream->flags |= STREAM_FLAG_STOPPED; + break; case MEStreamTick: value.vt = VT_EMPTY; hr = SUCCEEDED(IMFMediaEvent_GetValue(event, &value)) && value.vt == VT_I8 ? S_OK : E_UNEXPECTED; @@ -829,6 +883,9 @@ static HRESULT source_reader_media_stream_state_handler(struct source_reader *re LeaveCriticalSection(&reader->cs); + if (event_type == MEStreamStopped) + WakeAllConditionVariable(&reader->stop_event); + return S_OK; } @@ -858,6 +915,7 @@ static HRESULT WINAPI source_reader_stream_events_callback_Invoke(IMFAsyncCallba break; case MEStreamSeeked: case MEStreamStarted: + case MEStreamStopped: case MEStreamTick: case MEEndOfStream: hr = source_reader_media_stream_state_handler(reader, stream, event); @@ -871,7 +929,8 @@ static HRESULT WINAPI source_reader_stream_events_callback_Invoke(IMFAsyncCallba IMFMediaEvent_Release(event); - IMFMediaStream_BeginGetEvent(stream, iface, (IUnknown *)stream); + if (event_type != MEStreamStopped) + IMFMediaStream_BeginGetEvent(stream, iface, (IUnknown *)stream); return S_OK; } @@ -888,13 +947,13 @@ static const IMFAsyncCallbackVtbl stream_events_callback_vtbl = static ULONG WINAPI source_reader_async_commands_callback_AddRef(IMFAsyncCallback *iface) { struct source_reader *reader = impl_from_async_commands_callback_IMFAsyncCallback(iface); - return IMFSourceReader_AddRef(&reader->IMFSourceReader_iface); + return source_reader_addref(reader); } static ULONG WINAPI source_reader_async_commands_callback_Release(IMFAsyncCallback *iface) { struct source_reader *reader = impl_from_async_commands_callback_IMFAsyncCallback(iface); - return IMFSourceReader_Release(&reader->IMFSourceReader_iface); + return source_reader_release(reader); } static struct stream_response * media_stream_detach_response(struct source_reader *reader, struct stream_response *response) @@ -1326,51 +1385,52 @@ static HRESULT WINAPI src_reader_QueryInterface(IMFSourceReader *iface, REFIID r static ULONG WINAPI src_reader_AddRef(IMFSourceReader *iface) { struct source_reader *reader = impl_from_IMFSourceReader(iface); - ULONG refcount = InterlockedIncrement(&reader->refcount); + ULONG refcount = InterlockedIncrement(&reader->public_refcount); TRACE("%p, refcount %u.\n", iface, refcount); return refcount; } +static BOOL source_reader_is_source_stopped(const struct source_reader *reader) +{ + unsigned int i; + + if (reader->source_state != SOURCE_STATE_STOPPED) + return FALSE; + + for (i = 0; i < reader->stream_count; ++i) + { + if (reader->streams[i].stream && !(reader->streams[i].flags & STREAM_FLAG_STOPPED)) + return FALSE; + } + + return TRUE; +} + static ULONG WINAPI src_reader_Release(IMFSourceReader *iface) { struct source_reader *reader = impl_from_IMFSourceReader(iface); - ULONG refcount = InterlockedDecrement(&reader->refcount); - unsigned int i; + ULONG refcount = InterlockedDecrement(&reader->public_refcount); TRACE("%p, refcount %u.\n", iface, refcount); if (!refcount) { - if (reader->async_callback) - IMFSourceReaderCallback_Release(reader->async_callback); if (reader->flags & SOURCE_READER_SHUTDOWN_ON_RELEASE) IMFMediaSource_Shutdown(reader->source); - if (reader->descriptor) - IMFPresentationDescriptor_Release(reader->descriptor); - if (reader->attributes) - IMFAttributes_Release(reader->attributes); - IMFMediaSource_Release(reader->source); - - for (i = 0; i < reader->stream_count; ++i) + else if (SUCCEEDED(IMFMediaSource_Stop(reader->source))) { - struct media_stream *stream = &reader->streams[i]; + EnterCriticalSection(&reader->cs); - if (stream->stream) - IMFMediaStream_Release(stream->stream); - if (stream->current) - IMFMediaType_Release(stream->current); - if (stream->decoder.transform) - IMFTransform_Release(stream->decoder.transform); - if (stream->allocator) - IMFVideoSampleAllocatorEx_Release(stream->allocator); + while (!source_reader_is_source_stopped(reader)) + { + SleepConditionVariableCS(&reader->stop_event, &reader->cs, INFINITE); + } + + LeaveCriticalSection(&reader->cs); } - source_reader_release_responses(reader, NULL); - free(reader->streams); - MFUnlockWorkQueue(reader->queue); - DeleteCriticalSection(&reader->cs); - free(reader); + source_reader_release(reader); } return refcount; @@ -2277,6 +2337,7 @@ static HRESULT create_source_reader_from_source(IMFMediaSource *source, IMFAttri object->source_events_callback.lpVtbl = &source_events_callback_vtbl; object->stream_events_callback.lpVtbl = &stream_events_callback_vtbl; object->async_commands_callback.lpVtbl = &async_commands_callback_vtbl; + object->public_refcount = 1; object->refcount = 1; list_init(&object->responses); if (shutdown_on_release) @@ -2286,6 +2347,7 @@ static HRESULT create_source_reader_from_source(IMFMediaSource *source, IMFAttri InitializeCriticalSection(&object->cs); InitializeConditionVariable(&object->sample_event); InitializeConditionVariable(&object->state_event); + InitializeConditionVariable(&object->stop_event); if (FAILED(hr = IMFMediaSource_CreatePresentationDescriptor(object->source, &object->descriptor))) goto failed; diff --git a/dlls/mfreadwrite/tests/mfplat.c b/dlls/mfreadwrite/tests/mfplat.c index e3ff7f6e7aa..aec7d7456ca 100644 --- a/dlls/mfreadwrite/tests/mfplat.c +++ b/dlls/mfreadwrite/tests/mfplat.c @@ -865,7 +865,8 @@ todo_wine hr = IMFSourceReader_Flush(reader, MF_SOURCE_READER_ALL_STREAMS); ok(hr == S_OK, "Failed to flush all streams, hr %#x.\n", hr); - IMFSourceReader_Release(reader); + refcount = IMFSourceReader_Release(reader); + ok(!refcount, "Unexpected refcount %u.\n", refcount); /* Async mode. */ callback = create_async_callback(); diff --git a/dlls/mshtml/htmlevent.c b/dlls/mshtml/htmlevent.c index 7ed1dfd2adc..1bfeeebd5a7 100644 --- a/dlls/mshtml/htmlevent.c +++ b/dlls/mshtml/htmlevent.c @@ -1685,15 +1685,33 @@ static HRESULT WINAPI DOMMouseEvent_get_pageY(IDOMMouseEvent *iface, LONG *p) static HRESULT WINAPI DOMMouseEvent_get_layerX(IDOMMouseEvent *iface, LONG *p) { DOMEvent *This = impl_from_IDOMMouseEvent(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + nsresult nsres; + LONG r; + + TRACE("(%p)->(%p)\n", This, p); + + nsres = nsIDOMMouseEvent_GetLayerX(This->mouse_event, &r); + if(NS_FAILED(nsres)) + return E_FAIL; + + *p = r; + return S_OK; } static HRESULT WINAPI DOMMouseEvent_get_layerY(IDOMMouseEvent *iface, LONG *p) { DOMEvent *This = impl_from_IDOMMouseEvent(iface); - FIXME("(%p)->(%p)\n", This, p); - return E_NOTIMPL; + nsresult nsres; + LONG r; + + TRACE("(%p)->(%p)\n", This, p); + + nsres = nsIDOMMouseEvent_GetLayerY(This->mouse_event, &r); + if(NS_FAILED(nsres)) + return E_FAIL; + + *p = r; + return S_OK; } static HRESULT WINAPI DOMMouseEvent_get_which(IDOMMouseEvent *iface, USHORT *p) diff --git a/dlls/msvcrt/math.c b/dlls/msvcrt/math.c index 14afad5fb45..28794d1c7b8 100644 --- a/dlls/msvcrt/math.c +++ b/dlls/msvcrt/math.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "msvcrt.h" #include "winternl.h" @@ -64,11 +65,23 @@ 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; 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 e50f3e6991b..348eda2afbd 100644 --- a/dlls/msvcrt/msvcrt.h +++ b/dlls/msvcrt/msvcrt.h @@ -33,6 +33,7 @@ #undef strncpy #undef wcsncpy +extern BOOL erms_supported DECLSPEC_HIDDEN; extern BOOL sse2_supported DECLSPEC_HIDDEN; #define DBL80_MAX_10_EXP 4932 diff --git a/dlls/msvcrt/string.c b/dlls/msvcrt/string.c index d92b7a38d12..01c10cd7e36 100644 --- a/dlls/msvcrt/string.c +++ b/dlls/msvcrt/string.c @@ -2941,6 +2941,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 /********************************************************************* @@ -3075,6 +3082,78 @@ void * __cdecl _memccpy(void *dst, const void *src, int c, size_t n) return NULL; } +#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 ) + +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" + "add $0x20, " DEST_REG "\n\t" + "sub $0x20, " 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, -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 ) + +#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) { @@ -3116,8 +3195,27 @@ 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; + } +#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) { diff --git a/dlls/ntdll/thread.c b/dlls/ntdll/thread.c index 5fc956b1dc4..4cec391cf8b 100644 --- a/dlls/ntdll/thread.c +++ b/dlls/ntdll/thread.c @@ -240,7 +240,41 @@ void DECLSPEC_HIDDEN call_thread_func( PRTL_THREAD_START_ROUTINE entry, void *ar __ENDTRY } -#else /* __i386__ */ +#elif /* __i386__ */ defined(__x86_64__) && defined(__ASM_SEH_SUPPORTED) +EXCEPTION_DISPOSITION WINAPI call_thread_func_handler( EXCEPTION_RECORD *rec, ULONG64 frame, + CONTEXT *context, DISPATCHER_CONTEXT *dispatch ) +{ + EXCEPTION_POINTERS ep = { rec, context }; + + WARN( "Unhandled exception, calling filter.\n" ); + + switch (call_unhandled_exception_filter( &ep )) + { + case EXCEPTION_CONTINUE_SEARCH: + return ExceptionContinueSearch; + case EXCEPTION_CONTINUE_EXECUTION: + return ExceptionContinueExecution; + case EXCEPTION_EXECUTE_HANDLER: + break; + } + NtTerminateProcess( GetCurrentProcess(), rec->ExceptionCode ); + return ExceptionContinueExecution; +} + +extern void WINAPI RtlUserThreadStart( PRTL_THREAD_START_ROUTINE entry, void *arg ); +__ASM_GLOBAL_FUNC( RtlUserThreadStart, + "subq $0x28, %rsp\n\t" + __ASM_SEH(".seh_stackalloc 0x28\n\t") + __ASM_SEH(".seh_endprologue\n\t") + "movq %rdx,%r8\n\t" + "movq %rcx,%rdx\n\t" + "xorq %rcx,%rcx\n\t" + "movq pBaseThreadInitThunk(%rip),%r9\n\t" + "call *%r9\n\t" + "int3\n\t" + __ASM_SEH(".seh_handler call_thread_func_handler, @except\n\t") ) + +#else /* defined(__x86_64__) && defined(__ASM_SEH_SUPPORTED) */ void WINAPI RtlUserThreadStart( PRTL_THREAD_START_ROUTINE entry, void *arg ) { diff --git a/dlls/ntdll/unix/esync.c b/dlls/ntdll/unix/esync.c index 55c5695964d..4663374653a 100644 --- a/dlls/ntdll/unix/esync.c +++ b/dlls/ntdll/unix/esync.c @@ -526,6 +526,9 @@ NTSTATUS esync_set_event( HANDLE handle ) if ((ret = get_object( handle, &obj ))) return ret; event = obj->shm; + if (obj->type != ESYNC_MANUAL_EVENT && obj->type != ESYNC_AUTO_EVENT) + return STATUS_OBJECT_TYPE_MISMATCH; + if (obj->type == ESYNC_MANUAL_EVENT) { /* Acquire the spinlock. */ diff --git a/dlls/ntdll/unix/fsync.c b/dlls/ntdll/unix/fsync.c index 39d969f061d..0f19c63a5f2 100644 --- a/dlls/ntdll/unix/fsync.c +++ b/dlls/ntdll/unix/fsync.c @@ -112,6 +112,15 @@ static inline void futex_vector_set( struct futex_waitv *waitv, int *addr, int v waitv->__reserved = 0; } +static void simulate_sched_quantum(void) +{ + if (!fsync_simulate_sched_quantum) return; + /* futex wait is often very quick to resume a waiting thread when woken. + * That reveals synchonization bugs in some games which happen to work on + * Windows due to the waiting threads having some minimal delay to wake up. */ + usleep(0); +} + static inline int futex_wait_multiple( const struct futex_waitv *futexes, int count, const ULONGLONG *end ) { @@ -576,6 +585,9 @@ NTSTATUS fsync_set_event( HANDLE handle, LONG *prev ) if ((ret = get_object( handle, &obj ))) return ret; event = obj->shm; + if (obj->type != FSYNC_MANUAL_EVENT && obj->type != FSYNC_AUTO_EVENT) + return STATUS_OBJECT_TYPE_MISMATCH; + if (!(current = __atomic_exchange_n( &event->signaled, 1, __ATOMIC_SEQ_CST ))) futex_wake( &event->signaled, INT_MAX ); @@ -753,8 +765,8 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, struct futex_waitv futexes[MAXIMUM_WAIT_OBJECTS + 1]; struct fsync *objs[MAXIMUM_WAIT_OBJECTS]; + BOOL msgwait = FALSE, waited = FALSE; int has_fsync = 0, has_server = 0; - BOOL msgwait = FALSE; int dummy_futex = 0; unsigned int spin; LONGLONG timeleft; @@ -876,6 +888,7 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, && __sync_val_compare_and_swap( &semaphore->count, current, current - 1 ) == current) { TRACE("Woken up by handle %p [%d].\n", handles[i], i); + if (waited) simulate_sched_quantum(); return i; } small_pause(); @@ -893,6 +906,7 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, { TRACE("Woken up by handle %p [%d].\n", handles[i], i); mutex->count++; + if (waited) simulate_sched_quantum(); return i; } @@ -902,6 +916,7 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, { TRACE("Woken up by handle %p [%d].\n", handles[i], i); mutex->count = 1; + if (waited) simulate_sched_quantum(); return i; } else if (tid == ~0 && (tid = __sync_val_compare_and_swap( &mutex->tid, ~0, GetCurrentThreadId() )) == ~0) @@ -929,6 +944,7 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, usleep( 0 ); TRACE("Woken up by handle %p [%d].\n", handles[i], i); + if (waited) simulate_sched_quantum(); return i; } small_pause(); @@ -951,6 +967,7 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, usleep( 0 ); TRACE("Woken up by handle %p [%d].\n", handles[i], i); + if (waited) simulate_sched_quantum(); return i; } small_pause(); @@ -1005,6 +1022,7 @@ static NTSTATUS __fsync_wait_objects( DWORD count, const HANDLE *handles, TRACE("Wait timed out.\n"); return STATUS_TIMEOUT; } + else waited = TRUE; } /* while (1) */ } else diff --git a/dlls/ntdll/unix/loader.c b/dlls/ntdll/unix/loader.c index 1d4160727e4..f078a967be8 100644 --- a/dlls/ntdll/unix/loader.c +++ b/dlls/ntdll/unix/loader.c @@ -2237,33 +2237,30 @@ static struct unix_funcs unix_funcs = }; BOOL ac_odyssey; +BOOL fsync_simulate_sched_quantum; static void hacks_init(void) { + static const char upc_exe[] = "Ubisoft Game Launcher\\upc.exe"; static const char ac_odyssey_exe[] = "ACOdyssey.exe"; - char cur_exe[MAX_PATH]; - DWORD cur_exe_len; - const char *sgi; - int fd; - - fd = open("/proc/self/comm", O_RDONLY); - cur_exe_len = read(fd, cur_exe, sizeof(cur_exe)); - close(fd); - cur_exe[cur_exe_len - 1] = 0; + const char *env_str; - if (!strcasecmp(cur_exe, ac_odyssey_exe)) + if (main_argc > 1 && strstr(main_argv[1], ac_odyssey_exe)) { - if (do_esync() || do_fsync()) - ERR("HACK: AC Odyssey sync tweak on.\n"); - else - ERR("Not enabling AC Odyssey sync tweak as esync and fsync are disabled.\n"); - + ERR("HACK: AC Odyssey sync tweak on.\n"); ac_odyssey = TRUE; return; } + env_str = getenv("WINE_FSYNC_SIMULATE_SCHED_QUANTUM"); + if (env_str) + fsync_simulate_sched_quantum = !!atoi(env_str); + else if (main_argc > 1) + fsync_simulate_sched_quantum = !!strstr(main_argv[1], upc_exe); + if (fsync_simulate_sched_quantum) + ERR("HACK: Simulating sched quantum in fsync.\n"); - sgi = getenv("SteamGameId"); - if (sgi && !strcmp(sgi, "50130")) + env_str = getenv("SteamGameId"); + if (env_str && !strcmp(env_str, "50130")) setenv("WINESTEAMNOEXEC", "1", 0); } diff --git a/dlls/ntdll/unix/signal_arm.c b/dlls/ntdll/unix/signal_arm.c index eaa41d9e139..ff4dbedccac 100644 --- a/dlls/ntdll/unix/signal_arm.c +++ b/dlls/ntdll/unix/signal_arm.c @@ -835,6 +835,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) (DWORD)IP_sig(context), (DWORD)SP_sig(context), (DWORD)LR_sig(context), (DWORD)PC_sig(context), (DWORD)CPSR_sig(context) ); + if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION + && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) + ERR_(seh)( "Syscall stack overrun.\n "); + if (ntdll_get_thread_data()->jmp_buf) { TRACE( "returning to handler\n" ); diff --git a/dlls/ntdll/unix/signal_arm64.c b/dlls/ntdll/unix/signal_arm64.c index 26910173b81..ba3e3564c1a 100644 --- a/dlls/ntdll/unix/signal_arm64.c +++ b/dlls/ntdll/unix/signal_arm64.c @@ -860,6 +860,10 @@ static BOOL handle_syscall_fault( ucontext_t *context, EXCEPTION_RECORD *rec ) (DWORD64)REGn_sig(28, context), (DWORD64)FP_sig(context), (DWORD64)LR_sig(context), (DWORD64)SP_sig(context) ); + if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION + && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) + ERR_(seh)( "Syscall stack overrun.\n "); + if (ntdll_get_thread_data()->jmp_buf) { TRACE( "returning to handler\n" ); diff --git a/dlls/ntdll/unix/signal_i386.c b/dlls/ntdll/unix/signal_i386.c index 53da5d3ac59..a0afd33ea13 100644 --- a/dlls/ntdll/unix/signal_i386.c +++ b/dlls/ntdll/unix/signal_i386.c @@ -1757,6 +1757,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, void *stack_ptr, context->Ebp, context->Esp, context->SegCs, context->SegDs, context->SegEs, context->SegFs, context->SegGs, context->EFlags ); + if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION + && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) + ERR_(seh)( "Syscall stack overrun.\n "); + if (ntdll_get_thread_data()->jmp_buf) { TRACE( "returning to handler\n" ); diff --git a/dlls/ntdll/unix/signal_x86_64.c b/dlls/ntdll/unix/signal_x86_64.c index 7d7f9090986..b1cc682649b 100644 --- a/dlls/ntdll/unix/signal_x86_64.c +++ b/dlls/ntdll/unix/signal_x86_64.c @@ -2792,6 +2792,10 @@ static BOOL handle_syscall_fault( ucontext_t *sigcontext, EXCEPTION_RECORD *rec, TRACE_(seh)( " r12=%016lx r13=%016lx r14=%016lx r15=%016lx\n", context->R12, context->R13, context->R14, context->R15 ); + if (rec->ExceptionCode == STATUS_ACCESS_VIOLATION + && is_inside_syscall_stack_guard( (char *)rec->ExceptionInformation[1] )) + ERR_(seh)( "Syscall stack overrun.\n "); + if (ntdll_get_thread_data()->jmp_buf) { TRACE_(seh)( "returning to handler\n" ); diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c index af0c53d441e..58b7502ef06 100644 --- a/dlls/ntdll/unix/sync.c +++ b/dlls/ntdll/unix/sync.c @@ -77,6 +77,46 @@ static const char *debugstr_timeout( const LARGE_INTEGER *timeout ) return wine_dbgstr_longlong( timeout->QuadPart ); } +#ifndef __NR_clock_gettime64 +#define __NR_clock_gettime64 403 +#endif + +struct timespec64 +{ + long long tv_sec; + long long tv_nsec; +}; + +static inline int do_clock_gettime( clockid_t clock_id, ULONGLONG *ticks ) +{ + static int clock_gettime64_supported = -1; + struct timespec64 ts64; + struct timespec ts; + int ret; + + if (clock_gettime64_supported < 0) + { + if (!syscall( __NR_clock_gettime64, clock_id, &ts64 )) + { + clock_gettime64_supported = 1; + *ticks = ts64.tv_sec * (ULONGLONG)TICKSPERSEC + ts64.tv_nsec / 100; + return 0; + } + clock_gettime64_supported = 0; + } + + if (clock_gettime64_supported) + { + if (!(ret = syscall( __NR_clock_gettime64, clock_id, &ts64 ))) + *ticks = ts64.tv_sec * (ULONGLONG)TICKSPERSEC + ts64.tv_nsec / 100; + return ret; + } + + if (!(ret = clock_gettime( clock_id, &ts ))) + *ticks = ts.tv_sec * (ULONGLONG)TICKSPERSEC + ts.tv_nsec / 100; + return ret; +} + /* return a monotonic time counter, in Win32 ticks */ static inline ULONGLONG monotonic_counter(void) { @@ -91,13 +131,13 @@ static inline ULONGLONG monotonic_counter(void) #endif return mach_absolute_time() * timebase.numer / timebase.denom / 100; #elif defined(HAVE_CLOCK_GETTIME) - struct timespec ts; + ULONGLONG ticks; #if 0 - if (!clock_gettime( CLOCK_MONOTONIC_RAW, &ts )) - return ts.tv_sec * (ULONGLONG)TICKSPERSEC + ts.tv_nsec / 100; + if (!do_clock_gettime( CLOCK_MONOTONIC_RAW, &ticks )) + return ticks; #endif - if (!clock_gettime( CLOCK_MONOTONIC, &ts )) - return ts.tv_sec * (ULONGLONG)TICKSPERSEC + ts.tv_nsec / 100; + if (!do_clock_gettime( CLOCK_MONOTONIC, &ticks )) + return ticks; #endif gettimeofday( &now, 0 ); return ticks_from_time_t( now.tv_sec ) + now.tv_usec * 10 - server_start_time; diff --git a/dlls/ntdll/unix/unix_private.h b/dlls/ntdll/unix/unix_private.h index 5f072956087..4bf0e7cb84e 100644 --- a/dlls/ntdll/unix/unix_private.h +++ b/dlls/ntdll/unix/unix_private.h @@ -88,7 +88,8 @@ static const SIZE_T teb_size = 0x3800; /* TEB64 + TEB32 + debug info */ static const SIZE_T signal_stack_mask = 0xffff; static const SIZE_T signal_stack_size = 0x10000 - 0x3800; static const SIZE_T kernel_stack_size = 0x60000; -static const SIZE_T min_kernel_stack = 0x2000; +static const SIZE_T kernel_stack_guard_size = 0x1000; +static const SIZE_T min_kernel_stack = 0x3000; static const LONG teb_offset = 0x2000; #define FILE_WRITE_TO_END_OF_FILE ((LONGLONG)-1) @@ -147,6 +148,7 @@ extern struct ldt_copy __wine_ldt_copy DECLSPEC_HIDDEN; #endif extern BOOL ac_odyssey DECLSPEC_HIDDEN; +extern BOOL fsync_simulate_sched_quantum DECLSPEC_HIDDEN; extern void init_environment( int argc, char *argv[], char *envp[] ) DECLSPEC_HIDDEN; extern void init_startup_info(void) DECLSPEC_HIDDEN; @@ -330,6 +332,13 @@ static inline BOOL is_inside_signal_stack( void *ptr ) (char *)ptr < (char *)get_signal_stack() + signal_stack_size); } +static inline BOOL is_inside_syscall_stack_guard( const char *stack_ptr ) +{ + const char *kernel_stack = ntdll_get_thread_data()->kernel_stack; + + return (stack_ptr >= kernel_stack && stack_ptr < kernel_stack + kernel_stack_guard_size); +} + static inline void mutex_lock( pthread_mutex_t *mutex ) { if (!process_exiting) pthread_mutex_lock( mutex ); diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c index e7caf162ee1..10e2bf1477c 100644 --- a/dlls/ntdll/unix/virtual.c +++ b/dlls/ntdll/unix/virtual.c @@ -3225,6 +3225,7 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI SIZE_T commit_size, SIZE_T extra_size ) { struct file_view *view; + char *kernel_stack; NTSTATUS status; sigset_t sigset; SIZE_T size; @@ -3268,6 +3269,10 @@ NTSTATUS virtual_alloc_thread_stack( INITIAL_TEB *stack, ULONG_PTR zero_bits, SI delete_view( view ); goto done; } + /* setup kernel stack no access guard page */ + kernel_stack = (char *)view->base + view->size; + set_page_vprot( kernel_stack, kernel_stack_guard_size, VPROT_COMMITTED ); + mprotect_range( kernel_stack, kernel_stack_guard_size, 0, 0 ); } /* note: limit is lower than base since the stack grows down */ diff --git a/dlls/urlmon/binding.c b/dlls/urlmon/binding.c index 708b81194c7..a74970033b3 100644 --- a/dlls/urlmon/binding.c +++ b/dlls/urlmon/binding.c @@ -1177,14 +1177,6 @@ static HRESULT WINAPI InternetProtocolSink_ReportResult(IInternetProtocolSink *i TRACE("(%p)->(%08x %d %s)\n", This, hrResult, dwError, debugstr_w(szResult)); - /* Make sure we don't call stop_binding before available data has been reported, - as some custom protocol handlers call ReportResult within LockRequest */ - if (!(This->state & BINDING_LOCKED) && hrResult == S_OK) - { - This->download_state = END_DOWNLOAD; - return S_OK; - } - stop_binding(This, hrResult, szResult); IInternetProtocolEx_Terminate(&This->protocol->IInternetProtocolEx_iface, 0); diff --git a/dlls/user32/focus.c b/dlls/user32/focus.c index 2afe5667a04..f081c4b7ee6 100644 --- a/dlls/user32/focus.c +++ b/dlls/user32/focus.c @@ -40,7 +40,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(win); * * Change the focus window, sending the WM_SETFOCUS and WM_KILLFOCUS messages */ -static HWND set_focus_window( HWND hwnd, BOOL from_active ) +static HWND set_focus_window( HWND hwnd, BOOL from_active, BOOL force ) { HWND previous = 0, ime_default; BOOL ret; @@ -53,7 +53,7 @@ static HWND set_focus_window( HWND hwnd, BOOL from_active ) } SERVER_END_REQ; if (!ret) return 0; - if (previous == hwnd) return previous; + if (!force && hwnd == previous) return previous; if (previous) { @@ -90,7 +90,7 @@ static HWND set_focus_window( HWND hwnd, BOOL from_active ) static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) { HWND previous = GetActiveWindow(); - BOOL ret; + BOOL ret = FALSE; DWORD old_thread, new_thread; CBTACTIVATESTRUCT cbt; @@ -100,10 +100,13 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) return TRUE; } + if (prev) *prev = previous; + if (win_set_flags( hwnd, WIN_IS_ACTIVATING, 0 ) & WIN_IS_ACTIVATING) return TRUE; + /* call CBT hook chain */ cbt.fMouse = mouse; cbt.hWndActive = previous; - if (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) return FALSE; + if (HOOK_CallHooks( WH_CBT, HCBT_ACTIVATE, (WPARAM)hwnd, (LPARAM)&cbt, TRUE )) goto done; if (IsWindow(previous)) { @@ -115,13 +118,14 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) SERVER_START_REQ( set_active_window ) { req->handle = wine_server_user_handle( hwnd ); + req->internal_msg = WM_WINE_SETACTIVEWINDOW; if ((ret = !wine_server_call_err( req ))) previous = wine_server_ptr_handle( reply->previous ); } SERVER_END_REQ; - if (!ret) return FALSE; + if (!ret) goto done; if (prev) *prev = previous; - if (previous == hwnd) return TRUE; + if (previous == hwnd) goto done; if (hwnd) { @@ -129,7 +133,7 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) if (SendMessageW( hwnd, WM_QUERYNEWPALETTE, 0, 0 )) SendMessageTimeoutW( HWND_BROADCAST, WM_PALETTEISCHANGING, (WPARAM)hwnd, 0, SMTO_ABORTIFHUNG, 2000, NULL ); - if (!IsWindow(hwnd)) return FALSE; + if (!(ret = IsWindow( hwnd ))) goto done; } old_thread = previous ? GetWindowThreadProcessId( previous, NULL ) : 0; @@ -163,7 +167,9 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) if (IsWindow(hwnd)) { - SendMessageW( hwnd, WM_NCACTIVATE, (hwnd == GetForegroundWindow()), (LPARAM)previous ); + SendMessageW( hwnd, WM_NCACTIVATE, + (hwnd == GetForegroundWindow()) && !(win_get_flags(previous) & WIN_IS_ACTIVATING), + (LPARAM)previous ); SendMessageW( hwnd, WM_ACTIVATE, MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, IsIconic(hwnd) ), (LPARAM)previous ); @@ -188,11 +194,13 @@ static BOOL set_active_window( HWND hwnd, HWND *prev, BOOL mouse, BOOL focus ) if (hwnd == info.hwndActive) { if (!info.hwndFocus || !hwnd || GetAncestor( info.hwndFocus, GA_ROOT ) != hwnd) - set_focus_window( hwnd, TRUE ); + set_focus_window( hwnd, TRUE, FALSE ); } } - return TRUE; +done: + win_set_flags( hwnd, 0, WIN_IS_ACTIVATING ); + return ret; } @@ -219,12 +227,16 @@ static BOOL set_foreground_window( HWND hwnd, BOOL mouse ) if (ret && previous != hwnd) { if (send_msg_old) /* old window belongs to other thread */ - SendNotifyMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 ); + PostMessageW( previous, WM_WINE_SETACTIVEWINDOW, 0, 0 ); else if (send_msg_new) /* old window belongs to us but new one to other thread */ ret = set_active_window( 0, NULL, mouse, TRUE ); + /* already active, set_active_window will do no nothing */ + if (!send_msg_new && hwnd == GetActiveWindow()) + SendMessageW( hwnd, WM_NCACTIVATE, TRUE, (LPARAM)hwnd ); + if (send_msg_new) /* new window belongs to other thread */ - SendNotifyMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 ); + PostMessageW( hwnd, WM_WINE_SETACTIVEWINDOW, (WPARAM)hwnd, 0 ); else /* new window belongs to us */ ret = set_active_window( hwnd, NULL, mouse, TRUE ); } @@ -329,7 +341,7 @@ HWND WINAPI SetFocus( HWND hwnd ) } /* change focus and send messages */ - return set_focus_window( hwnd, FALSE ); + return set_focus_window( hwnd, FALSE, hwnd != previous ); } diff --git a/dlls/user32/message.c b/dlls/user32/message.c index 183912cd653..988ef999edb 100644 --- a/dlls/user32/message.c +++ b/dlls/user32/message.c @@ -1876,7 +1876,8 @@ static LRESULT handle_internal_message( HWND hwnd, UINT msg, WPARAM wparam, LPAR if (is_desktop_window( hwnd )) return 0; return WIN_SetStyle(hwnd, wparam, lparam); case WM_WINE_SETACTIVEWINDOW: - if (!wparam && GetForegroundWindow() == hwnd) return 0; + if (!wparam && GetWindowThreadProcessId( GetForegroundWindow(), NULL ) == GetCurrentThreadId()) + return 0; return (LRESULT)SetActiveWindow( (HWND)wparam ); case WM_WINE_KEYBOARD_LL_HOOK: case WM_WINE_MOUSE_LL_HOOK: diff --git a/dlls/user32/win.h b/dlls/user32/win.h index 45d3acce7bc..4e9dc9efd38 100644 --- a/dlls/user32/win.h +++ b/dlls/user32/win.h @@ -80,6 +80,7 @@ typedef struct tagWND #define WIN_CHILDREN_MOVED 0x0040 /* children may have moved, ignore stored positions */ #define WIN_HAS_IME_WIN 0x0080 /* the window has been registered with imm32 */ #define WIN_IS_TOUCH 0x0100 /* the window has been registered for touch input */ +#define WIN_IS_ACTIVATING 0x0200 /* the window is being activated */ /* Window functions */ extern HWND get_hwnd_message_parent(void) DECLSPEC_HIDDEN; diff --git a/dlls/userenv/tests/userenv.c b/dlls/userenv/tests/userenv.c index a91edeef260..c7a6791e2be 100644 --- a/dlls/userenv/tests/userenv.c +++ b/dlls/userenv/tests/userenv.c @@ -78,7 +78,7 @@ static void test_create_env(void) BOOL r, is_wow64 = FALSE; HANDLE htok; WCHAR * env[4]; - char * st, systemroot[100]; + char * st, systemroot[100], programdata[100]; int i, j; static const struct profile_item common_vars[] = { @@ -96,6 +96,7 @@ static void test_create_env(void) }; static const struct profile_item common_post_nt4_vars[] = { { "ALLUSERSPROFILE" }, + { "ProgramData" }, { "TEMP" }, { "TMP" }, { "CommonProgramFiles" }, @@ -119,6 +120,12 @@ static void test_create_env(void) r = SetEnvironmentVariableA("SystemRoot", "overwrite"); expect(TRUE, r); + r = GetEnvironmentVariableA("ProgramData", programdata, sizeof(programdata)); + ok(r != 0, "GetEnvironmentVariable failed (%d)\n", GetLastError()); + + r = SetEnvironmentVariableA("ProgramData", "overwrite"); + expect(TRUE, r); + if (0) { /* Crashes on NT4 */ @@ -151,12 +158,20 @@ static void test_create_env(void) r = SetEnvironmentVariableA("SystemRoot", systemroot); expect(TRUE, r); + r = SetEnvironmentVariableA("ProgramData", programdata); + expect(TRUE, r); + for(i=0; i<4; i++) { r = get_env(env[i], "SystemRoot", &st); ok(!strcmp(st, "SystemRoot=overwrite"), "%s\n", st); expect(TRUE, r); HeapFree(GetProcessHeap(), 0, st); + + r = get_env(env[i], "ProgramData", &st); + ok(strcmp(st, "ProgramData=overwrite"), "%s\n", st); + expect(TRUE, r); + HeapFree(GetProcessHeap(), 0, st); } /* Test for common environment variables (NT4 and higher) */ diff --git a/dlls/userenv/userenv_main.c b/dlls/userenv/userenv_main.c index 87ace7d5c11..08dede60d35 100644 --- a/dlls/userenv/userenv_main.c +++ b/dlls/userenv/userenv_main.c @@ -243,7 +243,10 @@ BOOL WINAPI CreateEnvironmentBlock( LPVOID* lpEnvironment, } if (get_reg_value(env, hkey, L"Public", buf, UNICODE_STRING_MAX_CHARS)) + { set_env_var(&env, L"ALLUSERSPROFILE", buf); + set_env_var(&env, L"ProgramData", buf); + } } else { diff --git a/dlls/winebus.sys/bus_sdl.c b/dlls/winebus.sys/bus_sdl.c index 26191ccb3a5..c5b45920d2e 100644 --- a/dlls/winebus.sys/bus_sdl.c +++ b/dlls/winebus.sys/bus_sdl.c @@ -187,12 +187,13 @@ static BOOL descriptor_add_haptic(struct sdl_device *impl) !(impl->sdl_haptic = pSDL_HapticOpenFromJoystick(impl->sdl_joystick))) impl->effect_support = 0; else + { impl->effect_support = pSDL_HapticQuery(impl->sdl_haptic); - - if (!(impl->effect_support & EFFECT_SUPPORT_HAPTICS) && - pSDL_HapticRumbleSupported(impl->sdl_haptic) && - pSDL_HapticRumbleInit(impl->sdl_haptic) == 0) - impl->effect_support |= WINE_SDL_HAPTIC_RUMBLE; + if (!(impl->effect_support & EFFECT_SUPPORT_HAPTICS) && + pSDL_HapticRumbleSupported(impl->sdl_haptic) && + pSDL_HapticRumbleInit(impl->sdl_haptic) == 0) + impl->effect_support |= WINE_SDL_HAPTIC_RUMBLE; + } if (!(impl->effect_support & EFFECT_SUPPORT_HAPTICS) && pSDL_JoystickRumble && !pSDL_JoystickRumble(impl->sdl_joystick, 0, 0, 0)) diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c index c2f8e163067..b0043c0de7e 100644 --- a/dlls/winebus.sys/unixlib.c +++ b/dlls/winebus.sys/unixlib.c @@ -72,6 +72,7 @@ BOOL is_sdl_blacklisted(DWORD vid, DWORD pid) BOOL is_steam_controller(WORD vid, WORD pid) { + if (vid == 0x2808 && pid == 0x1015) return TRUE; /* Steam Deck touchscreen */ if (vid != 0x28de) return FALSE; if (pid == 0x1101) return TRUE; /* Valve Legacy Steam Controller */ if (pid == 0x1102) return TRUE; /* Valve wired Steam Controller */ @@ -80,6 +81,7 @@ BOOL is_steam_controller(WORD vid, WORD pid) if (pid == 0x1142) return TRUE; /* Valve wireless Steam Controller */ if (pid == 0x1201) return TRUE; /* Valve wired Steam Controller */ if (pid == 0x1202) return TRUE; /* Valve Bluetooth Steam Controller */ + if (pid == 0x1205) return TRUE; /* Valve Steam Deck Mouse / Keyboard */ return FALSE; } diff --git a/dlls/wined3d/context_gl.c b/dlls/wined3d/context_gl.c index 9fd2ed67dcc..8e8305352fe 100644 --- a/dlls/wined3d/context_gl.c +++ b/dlls/wined3d/context_gl.c @@ -1897,7 +1897,7 @@ HGLRC context_create_wgl_attribs(const struct wined3d_gl_info *gl_info, HDC hdc, } static BOOL wined3d_context_gl_create_wgl_ctx(struct wined3d_context_gl *context_gl, - struct wined3d_swapchain_gl *swapchain_gl) + struct wined3d_swapchain_gl *swapchain_gl, BOOL *new_drawable) { enum wined3d_swap_effect swap_effect = swapchain_gl->s.state.desc.swap_effect; const struct wined3d_format *colour_format, *ds_format; @@ -1920,6 +1920,8 @@ static BOOL wined3d_context_gl_create_wgl_ctx(struct wined3d_context_gl *context swap_effect_copy = swap_effect == WINED3D_SWAP_EFFECT_COPY || swap_effect == WINED3D_SWAP_EFFECT_COPY_VSYNC; + *new_drawable = !GetPixelFormat(context_gl->dc); + if (wined3d_settings.offscreen_rendering_mode == ORM_BACKBUFFER) { static const enum wined3d_format_id ds_formats[] = @@ -2018,7 +2020,7 @@ static BOOL wined3d_context_gl_create_wgl_ctx(struct wined3d_context_gl *context } context_gl->dc_is_private = TRUE; - return wined3d_context_gl_create_wgl_ctx(context_gl, swapchain_gl); + return wined3d_context_gl_create_wgl_ctx(context_gl, swapchain_gl, new_drawable); } share_ctx = device->context_count ? wined3d_context_gl(device->contexts[0])->gl_ctx : NULL; @@ -2064,6 +2066,7 @@ HRESULT wined3d_context_gl_init(struct wined3d_context_gl *context_gl, struct wi const struct wined3d_d3d_info *d3d_info; const struct wined3d_gl_info *gl_info; struct wined3d_device *device; + BOOL new_drawable; unsigned int i; TRACE("context_gl %p, swapchain %p.\n", context_gl, swapchain_gl); @@ -2142,7 +2145,7 @@ HRESULT wined3d_context_gl_init(struct wined3d_context_gl *context_gl, struct wi sizeof(*context_gl->texture_type)))) goto fail; - if (!wined3d_context_gl_create_wgl_ctx(context_gl, swapchain_gl)) + if (!wined3d_context_gl_create_wgl_ctx(context_gl, swapchain_gl, &new_drawable)) goto fail; /* Set up the context defaults. */ @@ -2299,6 +2302,14 @@ HRESULT wined3d_context_gl_init(struct wined3d_context_gl *context_gl, struct wi gl_info->gl_ops.gl.p_glScissor(0, 0, 0, 0); checkGLcall("glScissor"); + if (new_drawable) + { + gl_info->gl_ops.gl.p_glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + gl_info->gl_ops.gl.p_glClearDepth(1.0f); + gl_info->gl_ops.gl.p_glClearStencil(0); + gl_info->gl_ops.gl.p_glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + checkGLcall("glClear"); + } return WINED3D_OK; fail: diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c index 49a40f3e1d6..5fcbfc58779 100644 --- a/dlls/wined3d/swapchain.c +++ b/dlls/wined3d/swapchain.c @@ -459,8 +459,9 @@ static void swapchain_blit_gdi(struct wined3d_swapchain *swapchain, if (!(dst_dc = GetDCEx(swapchain->win_handle, 0, DCX_USESTYLE | DCX_CACHE))) ERR("Failed to get destination DC.\n"); - if (!BitBlt(dst_dc, dst_rect->left, dst_rect->top, dst_rect->right - dst_rect->left, - dst_rect->bottom - dst_rect->top, src_dc, src_rect->left, src_rect->top, SRCCOPY)) + if (!StretchBlt(dst_dc, dst_rect->left, dst_rect->top, dst_rect->right - dst_rect->left, + dst_rect->bottom - dst_rect->top, src_dc, src_rect->left, src_rect->top, + src_rect->right - src_rect->left, src_rect->bottom - src_rect->top, SRCCOPY)) ERR("Failed to blit.\n"); ReleaseDC(swapchain->win_handle, dst_dc); diff --git a/dlls/winegstreamer/gst_private.h b/dlls/winegstreamer/gst_private.h index 3b456aea3a2..4d9b9cb10fe 100644 --- a/dlls/winegstreamer/gst_private.h +++ b/dlls/winegstreamer/gst_private.h @@ -82,7 +82,7 @@ uint32_t wg_parser_get_stream_count(struct wg_parser *parser) DECLSPEC_HIDDEN; struct wg_parser_stream *wg_parser_get_stream(struct wg_parser *parser, uint32_t index) DECLSPEC_HIDDEN; void wg_parser_stream_get_preferred_format(struct wg_parser_stream *stream, struct wg_format *format) DECLSPEC_HIDDEN; -void wg_parser_stream_enable(struct wg_parser_stream *stream, const struct wg_format *format, const struct wg_rect *aperture) DECLSPEC_HIDDEN; +void wg_parser_stream_enable(struct wg_parser_stream *stream, const struct wg_format *format, const struct wg_rect *aperture, uint32_t flags) DECLSPEC_HIDDEN; void wg_parser_stream_disable(struct wg_parser_stream *stream) DECLSPEC_HIDDEN; bool wg_parser_stream_get_event(struct wg_parser_stream *stream, struct wg_parser_event *event) DECLSPEC_HIDDEN; diff --git a/dlls/winegstreamer/h264_decoder.c b/dlls/winegstreamer/h264_decoder.c index daa1a738c38..f6a4d47188f 100644 --- a/dlls/winegstreamer/h264_decoder.c +++ b/dlls/winegstreamer/h264_decoder.c @@ -520,7 +520,19 @@ static HRESULT WINAPI h264_decoder_ProcessEvent(IMFTransform *iface, DWORD id, I static HRESULT WINAPI h264_decoder_ProcessMessage(IMFTransform *iface, MFT_MESSAGE_TYPE message, ULONG_PTR param) { + struct h264_decoder *decoder = impl_from_IMFTransform(iface); + FIXME("iface %p, message %#x, param %p stub!\n", iface, message, (void *)param); + + switch (message) + { + case MFT_MESSAGE_NOTIFY_BEGIN_STREAMING: + memset(&decoder->wg_format, 0, sizeof(decoder->wg_format)); + break; + default: + break; + } + return S_OK; } diff --git a/dlls/winegstreamer/main.c b/dlls/winegstreamer/main.c index 60651541dbc..8c6d8268541 100644 --- a/dlls/winegstreamer/main.c +++ b/dlls/winegstreamer/main.c @@ -180,13 +180,14 @@ void wg_parser_stream_get_preferred_format(struct wg_parser_stream *stream, stru __wine_unix_call(unix_handle, unix_wg_parser_stream_get_preferred_format, ¶ms); } -void wg_parser_stream_enable(struct wg_parser_stream *stream, const struct wg_format *format, const struct wg_rect *aperture) +void wg_parser_stream_enable(struct wg_parser_stream *stream, const struct wg_format *format, const struct wg_rect *aperture, uint32_t flags) { struct wg_parser_stream_enable_params params = { .stream = stream, .format = format, .aperture = aperture, + .flags = flags, }; __wine_unix_call(unix_handle, unix_wg_parser_stream_enable, ¶ms); diff --git a/dlls/winegstreamer/media_source.c b/dlls/winegstreamer/media_source.c index 7d28fb26636..a41205c6e34 100644 --- a/dlls/winegstreamer/media_source.c +++ b/dlls/winegstreamer/media_source.c @@ -357,7 +357,7 @@ static void start_pipeline(struct media_source *source, struct source_async_comm IMFMediaTypeHandler_GetCurrentMediaType(mth, ¤t_mt); mf_media_type_to_wg_format(current_mt, &format); - wg_parser_stream_enable(stream->wg_stream, &format, NULL); + wg_parser_stream_enable(stream->wg_stream, &format, NULL, 0); IMFMediaType_Release(current_mt); IMFMediaTypeHandler_Release(mth); @@ -872,7 +872,7 @@ static HRESULT new_media_stream(struct media_source *source, static HRESULT media_stream_init_desc(struct media_stream *stream) { IMFMediaTypeHandler *type_handler = NULL; - IMFMediaType *stream_types[7]; + IMFMediaType *stream_types[8]; struct wg_format format; DWORD type_count = 0; unsigned int i; @@ -892,6 +892,7 @@ static HRESULT media_stream_init_desc(struct media_stream *stream) &MFVideoFormat_IYUV, &MFVideoFormat_I420, &MFVideoFormat_ARGB32, + &MFVideoFormat_RGB32, }; IMFMediaType *base_type = mf_media_type_from_wg_format(&format); diff --git a/dlls/winegstreamer/quartz_parser.c b/dlls/winegstreamer/quartz_parser.c index 2782f773beb..a58fa39e87e 100644 --- a/dlls/winegstreamer/quartz_parser.c +++ b/dlls/winegstreamer/quartz_parser.c @@ -1179,7 +1179,7 @@ HRESULT decodebin_parser_create(IUnknown *outer, IUnknown **out) if (!(object = calloc(1, sizeof(*object)))) return E_OUTOFMEMORY; - if (!(object->wg_parser = wg_parser_create(WG_PARSER_DECODEBIN, false))) + if (!(object->wg_parser = wg_parser_create(WG_PARSER_DECODEBIN, true))) { free(object); return E_OUTOFMEMORY; @@ -1533,7 +1533,7 @@ static HRESULT WINAPI GSTOutPin_DecideBufferSize(struct strmbase_source *iface, ret = amt_to_wg_format(&pin->pin.pin.mt, &format); assert(ret); - wg_parser_stream_enable(pin->wg_stream, &format, NULL); + wg_parser_stream_enable(pin->wg_stream, &format, NULL, STREAM_ENABLE_FLAG_FLIP_RGB); /* We do need to drop any buffers that might have been sent with the old * caps, but this will be handled in parser_init_stream(). */ diff --git a/dlls/winegstreamer/unixlib.h b/dlls/winegstreamer/unixlib.h index 79f68695fd8..0be6d267c1f 100644 --- a/dlls/winegstreamer/unixlib.h +++ b/dlls/winegstreamer/unixlib.h @@ -243,11 +243,14 @@ struct wg_parser_stream_get_preferred_format_params struct wg_format *format; }; +#define STREAM_ENABLE_FLAG_FLIP_RGB 0x1 + struct wg_parser_stream_enable_params { struct wg_parser_stream *stream; const struct wg_format *format; const struct wg_rect *aperture; + uint32_t flags; }; struct wg_parser_stream_get_event_params diff --git a/dlls/winegstreamer/wg_parser.c b/dlls/winegstreamer/wg_parser.c index 178c47d5d32..f7c319d3c8f 100644 --- a/dlls/winegstreamer/wg_parser.c +++ b/dlls/winegstreamer/wg_parser.c @@ -30,6 +30,7 @@ #include #include +#define GLIB_VERSION_MIN_REQUIRED GLIB_VERSION_2_30 #include #include #include @@ -668,31 +669,34 @@ static NTSTATUS wg_parser_stream_enable(void *args) if (format->major_type == WG_MAJOR_TYPE_VIDEO) { - bool flip = (format->u.video.height < 0); - - switch (format->u.video.format) + if (params->flags & STREAM_ENABLE_FLAG_FLIP_RGB) { - case WG_VIDEO_FORMAT_BGRA: - case WG_VIDEO_FORMAT_BGRx: - case WG_VIDEO_FORMAT_BGR: - case WG_VIDEO_FORMAT_RGB15: - case WG_VIDEO_FORMAT_RGB16: - flip = !flip; - break; + bool flip = (format->u.video.height < 0); - case WG_VIDEO_FORMAT_AYUV: - case WG_VIDEO_FORMAT_I420: - case WG_VIDEO_FORMAT_NV12: - case WG_VIDEO_FORMAT_UYVY: - case WG_VIDEO_FORMAT_YUY2: - case WG_VIDEO_FORMAT_YV12: - case WG_VIDEO_FORMAT_YVYU: - case WG_VIDEO_FORMAT_UNKNOWN: - case WG_VIDEO_FORMAT_CINEPAK: - break; - } + switch (format->u.video.format) + { + case WG_VIDEO_FORMAT_BGRA: + case WG_VIDEO_FORMAT_BGRx: + case WG_VIDEO_FORMAT_BGR: + case WG_VIDEO_FORMAT_RGB15: + case WG_VIDEO_FORMAT_RGB16: + flip = !flip; + break; - gst_util_set_object_arg(G_OBJECT(stream->flip), "method", flip ? "vertical-flip" : "none"); + case WG_VIDEO_FORMAT_AYUV: + case WG_VIDEO_FORMAT_I420: + case WG_VIDEO_FORMAT_NV12: + case WG_VIDEO_FORMAT_UYVY: + case WG_VIDEO_FORMAT_YUY2: + case WG_VIDEO_FORMAT_YV12: + case WG_VIDEO_FORMAT_YVYU: + case WG_VIDEO_FORMAT_UNKNOWN: + case WG_VIDEO_FORMAT_CINEPAK: + break; + } + + gst_util_set_object_arg(G_OBJECT(stream->flip), "method", flip ? "vertical-flip" : "none"); + } if (aperture) { diff --git a/dlls/winegstreamer/wm_reader.c b/dlls/winegstreamer/wm_reader.c index 905ce7306c8..98847c4b306 100644 --- a/dlls/winegstreamer/wm_reader.c +++ b/dlls/winegstreamer/wm_reader.c @@ -1520,7 +1520,7 @@ static HRESULT init_stream(struct wm_reader *reader, QWORD file_size) stream->format.u.video.format = WG_VIDEO_FORMAT_BGRx; } } - wg_parser_stream_enable(stream->wg_stream, &stream->format, NULL); + wg_parser_stream_enable(stream->wg_stream, &stream->format, NULL, STREAM_ENABLE_FLAG_FLIP_RGB); } wg_parser_end_flush(reader->wg_parser); @@ -1787,7 +1787,7 @@ HRESULT wm_reader_set_output_props(struct wm_reader *reader, DWORD output, } stream->format = format; - wg_parser_stream_enable(stream->wg_stream, &format, NULL); + wg_parser_stream_enable(stream->wg_stream, &format, NULL, STREAM_ENABLE_FLAG_FLIP_RGB); /* Re-decode any buffers that might have been generated with the old format. * @@ -2000,7 +2000,7 @@ HRESULT wm_reader_set_streams_selected(struct wm_reader *reader, WORD count, FIXME("Ignoring selection %#x for stream %u; treating as enabled.\n", selections[i], stream_numbers[i]); TRACE("Enabling stream %u.\n", stream_numbers[i]); - wg_parser_stream_enable(stream->wg_stream, &stream->format, NULL); + wg_parser_stream_enable(stream->wg_stream, &stream->format, NULL, STREAM_ENABLE_FLAG_FLIP_RGB); } } diff --git a/dlls/winevulkan/make_vulkan b/dlls/winevulkan/make_vulkan index 16a73a74aeb..a0287c1ff09 100755 --- a/dlls/winevulkan/make_vulkan +++ b/dlls/winevulkan/make_vulkan @@ -263,8 +263,6 @@ FUNCTION_OVERRIDES = { # VK_EXT_debug_report "vkCreateDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, "vkDestroyDebugReportCallbackEXT" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, - - "vkCmdWriteBufferMarkerAMD" : {"dispatch": True, "driver" : False, "thunk" : ThunkType.NONE}, } STRUCT_CHAIN_CONVERSIONS = { diff --git a/dlls/winevulkan/vulkan.c b/dlls/winevulkan/vulkan.c index e0da6849d73..3c8bd452eca 100644 --- a/dlls/winevulkan/vulkan.c +++ b/dlls/winevulkan/vulkan.c @@ -2656,32 +2656,6 @@ NTSTATUS wine_vkDestroyDebugReportCallbackEXT(void *args) return STATUS_SUCCESS; } -/* HACK: Rainbow Six Siege tries to call this function regardless of whether the extension is exposed */ -NTSTATUS wine_vkCmdWriteBufferMarkerAMD(void *args) -{ - struct vkCmdWriteBufferMarkerAMD_params *params = args; - VkCommandBuffer commandBuffer = params->commandBuffer; - VkPipelineStageFlagBits pipelineStage = params->pipelineStage; - VkBuffer dstBuffer = params->dstBuffer; - VkDeviceSize dstOffset = params->dstOffset; - uint32_t marker = params->marker; - - TRACE("%p, %#x, 0x%s, 0x%s, %u\n", commandBuffer, pipelineStage, wine_dbgstr_longlong(dstBuffer), wine_dbgstr_longlong(dstOffset), marker); - - if (commandBuffer->device->funcs.p_vkCmdWriteBufferMarkerAMD) - { - commandBuffer->device->funcs.p_vkCmdWriteBufferMarkerAMD(commandBuffer->command_buffer, pipelineStage, dstBuffer, dstOffset, marker); - } - else - { - static unsigned int once; - - if (!once++) - FIXME("HACK: returning success from unexposed function.\n"); - } - return STATUS_SUCCESS; -} - NTSTATUS wine_vkAcquireNextImage2KHR(void *args) { struct vkAcquireNextImage2KHR_params *params = args; diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c index ce81dd3c084..27d1fa7fbe0 100644 --- a/dlls/winex11.drv/event.c +++ b/dlls/winex11.drv/event.c @@ -340,6 +340,7 @@ static int try_grab_pointer( Display *display ) return 0; XUngrabPointer( display, CurrentTime ); + XFlush( display ); return 1; } @@ -845,6 +846,14 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (event->mode == NotifyUngrab && wm_is_mutter(event->display)) Sleep(100); + if (!try_grab_pointer( event->display )) + { + /* ask the desktop window to release its grab before trying to get ours */ + SendMessageW( GetDesktopWindow(), WM_X11DRV_RELEASE_CURSOR, 0, 0 ); + XSendEvent( event->display, event->window, False, 0, xev ); + return FALSE; + } + /* ask the foreground window to re-apply the current ClipCursor rect */ SendMessageW( GetForegroundWindow(), WM_X11DRV_CLIP_CURSOR_REQUEST, 0, 0 ); @@ -867,11 +876,6 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev ) if (hwnd && can_activate_window(hwnd)) set_focus( xev, hwnd, CurrentTime ); return TRUE; } - else if (!try_grab_pointer( event->display )) - { - XSendEvent( event->display, event->window, False, 0, xev ); - return FALSE; - } SetForegroundWindow( hwnd ); return TRUE; diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c index 82906303a85..a3377a659c3 100644 --- a/dlls/winex11.drv/opengl.c +++ b/dlls/winex11.drv/opengl.c @@ -2254,6 +2254,50 @@ static void fs_hack_setup_gamma_shader( struct wgl_context *ctx, struct gl_drawa pglUseProgram( prev_program ); } +enum fshack_texture_type +{ + FSHACK_TEXTURE_COLOUR, + FSHACK_TEXTURE_DEPTH, + FSHACK_TEXTURE_LAST, +}; + +static void gen_texture( struct wgl_context *ctx, GLuint *tex, enum fshack_texture_type type ) +{ + static const GLuint texture_names[FSHACK_TEXTURE_LAST] = + { + 65535, + 65536, + }; + static int texture_name_hack = -1; + static int once; + + if (ctx->is_core) + { + opengl_funcs.gl.p_glGenTextures( 1, tex ); + return; + } + + if (texture_name_hack == -1) + { + const char *sgi = getenv("SteamGameId"); + + texture_name_hack = sgi && !strcmp( sgi, "6020" ); + } + + if (!texture_name_hack || opengl_funcs.gl.p_glIsTexture( texture_names[type] )) + { + if (texture_name_hack) + FIXME( "Texture %u already exists.\n", texture_names[type] ); + opengl_funcs.gl.p_glGenTextures( 1, tex ); + return; + } + /* Star Wars Jedi Knight: Jedi Academy uses texture names without allocating + * them with glGenTextures(). Trying to use a texture name which has low chances + * to overlap with what games may use. */ + if (!once++) FIXME( "Using texture name hack.\n" ); + *tex = texture_names[type]; +} + static void fs_hack_setup_context( struct wgl_context *ctx, struct gl_drawable *gl ) { GLuint prev_draw_fbo, prev_read_fbo, prev_texture, prev_renderbuffer; @@ -2325,7 +2369,8 @@ static void fs_hack_setup_context( struct wgl_context *ctx, struct gl_drawable * fs_hack_get_attachments_config( gl, &attribs, &config ); if (!ctx->fs_hack_color_texture) - opengl_funcs.gl.p_glGenTextures( 1, &ctx->fs_hack_color_texture ); + gen_texture( ctx, &ctx->fs_hack_color_texture, FSHACK_TEXTURE_COLOUR ); + opengl_funcs.gl.p_glBindTexture( GL_TEXTURE_2D, ctx->fs_hack_color_texture ); opengl_funcs.gl.p_glTexImage2D( GL_TEXTURE_2D, 0, config.color_internalformat, width, height, 0, config.color_format, config.color_type, NULL); @@ -2389,7 +2434,8 @@ static void fs_hack_setup_context( struct wgl_context *ctx, struct gl_drawable * else { if (!ctx->fs_hack_ds_texture) - opengl_funcs.gl.p_glGenTextures( 1, &ctx->fs_hack_ds_texture ); + gen_texture( ctx, &ctx->fs_hack_ds_texture, FSHACK_TEXTURE_DEPTH ); + opengl_funcs.gl.p_glBindTexture( GL_TEXTURE_2D, ctx->fs_hack_ds_texture ); opengl_funcs.gl.p_glTexImage2D( GL_TEXTURE_2D, 0, config.ds_internalformat, width, height, 0, config.ds_format, config.ds_type, NULL); @@ -2408,12 +2454,25 @@ static void fs_hack_setup_context( struct wgl_context *ctx, struct gl_drawable * if (!ctx->has_been_current) opengl_funcs.gl.p_glViewport(0, 0, width, height); - if(!gl->fs_hack_context_set_up) + if (!gl->fs_hack_context_set_up) { - opengl_funcs.gl.p_glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); - opengl_funcs.gl.p_glClearDepth( 1.0 ); - opengl_funcs.gl.p_glClearStencil( 0 ); - opengl_funcs.gl.p_glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); + if (ctx->has_been_current) + { + GLbitfield mask = GL_COLOR_BUFFER_BIT; + + if (attribs.depth_size) mask |= GL_DEPTH_BUFFER_BIT; + if (attribs.stencil_size) mask |= GL_STENCIL_BUFFER_BIT; + + pglBindFramebuffer( GL_READ_FRAMEBUFFER, 0 ); + pglBlitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, GL_NEAREST ); + } + else + { + opengl_funcs.gl.p_glClearColor( 0.0f, 0.0f, 0.0f, 1.0f ); + opengl_funcs.gl.p_glClearDepth( 1.0 ); + opengl_funcs.gl.p_glClearStencil( 0 ); + opengl_funcs.gl.p_glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT ); + } } pglBindFramebuffer( GL_DRAW_FRAMEBUFFER, 0 ); pglDrawBuffer( GL_BACK ); @@ -3228,6 +3287,21 @@ static struct wgl_context *X11DRV_wglCreateContextAttribsARB( HDC hdc, struct wg case WGL_CONTEXT_LAYER_PLANE_ARB: break; case WGL_CONTEXT_FLAGS_ARB: + /* HACK: The Last Campfire sometimes uses an + * invalid value for WGL_CONTEXT_FLAGS_ARB, which + * triggers + * https://gitlab.freedesktop.org/xorg/lib/libx11/-/issues/152 + * on the Deck. If we see the invalid value we + * directly return an error, so that Wine doesn't + * crash. This hack can be removed once that issue + * is fixed. */ + if (attribList[1] == 0x31b3) + { + WARN("return early to avoid triggering a libX11 bug\n"); + HeapFree(GetProcessHeap(), 0, ret); + release_gl_drawable(gl); + return NULL; + } pContextAttribList[0] = GLX_CONTEXT_FLAGS_ARB; pContextAttribList[1] = attribList[1]; pContextAttribList += 2; diff --git a/dlls/winhttp/tests/winhttp.c b/dlls/winhttp/tests/winhttp.c index 7e46a43aa05..053f8315fec 100644 --- a/dlls/winhttp/tests/winhttp.c +++ b/dlls/winhttp/tests/winhttp.c @@ -4816,7 +4816,7 @@ static void test_IWinHttpRequest_Invoke(void) VariantInit(&arg[2]); params.cArgs = 3; hr = IWinHttpRequest_Invoke(request, DISPID_HTTPREQUEST_OPTION, &IID_NULL, 0, DISPATCH_PROPERTYPUT, ¶ms, &ret, NULL, &err); -todo_wine + todo_wine ok(hr == S_OK, "error %#x\n", hr); VariantInit(&arg[0]); @@ -4861,7 +4861,7 @@ todo_wine params.cArgs = 2; hr = IWinHttpRequest_Invoke(request, DISPID_HTTPREQUEST_OPTION, &IID_NULL, 0, DISPATCH_PROPERTYGET, ¶ms, NULL, NULL, NULL); -todo_wine + todo_wine ok(hr == S_OK, "error %#x\n", hr); params.cArgs = 0; diff --git a/dlls/winhttp/url.c b/dlls/winhttp/url.c index 0d9dfb76de6..8efd5ed0c49 100644 --- a/dlls/winhttp/url.c +++ b/dlls/winhttp/url.c @@ -172,7 +172,7 @@ static DWORD parse_port( const WCHAR *str, DWORD len, INTERNET_PORT *ret ) */ BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONENTSW uc ) { - WCHAR *p, *q, *r, *url_decoded = NULL, *url_escaped = NULL; + WCHAR *p, *q, *r, *url_transformed = NULL; INTERNET_SCHEME scheme_number = 0; struct url_component scheme, username, password, hostname, path, extra; BOOL overflow = FALSE; @@ -189,25 +189,26 @@ BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONEN if (flags & ICU_ESCAPE) { - if ((err = escape_url( url, &len, &url_escaped ))) + if ((err = escape_url( url, &len, &url_transformed ))) { SetLastError( err ); return FALSE; } - url = url_escaped; + url = url_transformed; } else if (flags & ICU_DECODE) { - if (!(url_decoded = decode_url( url, &len ))) + if (!(url_transformed = decode_url( url, &len ))) { SetLastError( ERROR_OUTOFMEMORY ); return FALSE; } - url = url_decoded; + url = url_transformed; } if (!(p = wcschr( url, ':' ))) { SetLastError( ERROR_WINHTTP_UNRECOGNIZED_SCHEME ); + free( url_transformed ); return FALSE; } if (p - url == 4 && !wcsnicmp( url, L"http", 4 )) scheme_number = INTERNET_SCHEME_HTTP; @@ -331,8 +332,7 @@ BOOL WINAPI WinHttpCrackUrl( LPCWSTR url, DWORD len, DWORD flags, LPURL_COMPONEN if (overflow) err = ERROR_INSUFFICIENT_BUFFER; uc->nScheme = scheme_number; } - free( url_decoded ); - free( url_escaped ); + free( url_transformed ); SetLastError( err ); return !err; } diff --git a/dlls/winmm/waveform.c b/dlls/winmm/waveform.c index 1159b48b483..ecffa700662 100644 --- a/dlls/winmm/waveform.c +++ b/dlls/winmm/waveform.c @@ -191,6 +191,7 @@ static LRESULT WOD_Close(HWAVEOUT hwave); static LRESULT WID_Open(WINMM_OpenInfo *info); static LRESULT WID_Close(HWAVEIN hwave); static MMRESULT WINMM_BeginPlaying(WINMM_Device *device); +static void WOD_PushData(WINMM_Device *device); void WINMM_DeleteWaveform(void) { @@ -775,8 +776,11 @@ static HRESULT reroute_mapper_device(WINMM_Device *device, BOOL is_out) HeapFree(GetProcessHeap(), 0, info.format); - if(!stopped) + if(!stopped){ + if(is_out) + WOD_PushData(device); WINMM_BeginPlaying(device); + } LeaveCriticalSection(&device->lock); @@ -1948,10 +1952,6 @@ static MMRESULT WINMM_BeginPlaying(WINMM_Device *device) TRACE("(%p)\n", device->handle); - if(device->render) - /* prebuffer data before starting */ - WOD_PushData(device); - if(device->stopped){ device->stopped = FALSE; @@ -2893,6 +2893,9 @@ UINT WINAPI waveOutWrite(HWAVEOUT hWaveOut, WAVEHDR *header, UINT uSize) header->dwFlags &= ~WHDR_DONE; header->dwFlags |= WHDR_INQUEUE; + if(device->stopped) + WOD_PushData(device); + mr = WINMM_BeginPlaying(device); LeaveCriticalSection(&device->lock); @@ -2970,6 +2973,9 @@ UINT WINAPI waveOutRestart(HWAVEOUT hWaveOut) device->stopped = TRUE; + if(device->render) + WOD_PushData(device); + mr = WINMM_BeginPlaying(device); LeaveCriticalSection(&device->lock); diff --git a/include/wine/asm.h b/include/wine/asm.h index 0547ee94b19..9200491afd0 100644 --- a/include/wine/asm.h +++ b/include/wine/asm.h @@ -50,6 +50,7 @@ # define __ASM_SEH(str) # else # define __ASM_SEH(str) str +# define __ASM_SEH_SUPPORTED # endif #else # define __ASM_SEH(str) diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h index 1d17a40530f..d67489de1f0 100644 --- a/include/wine/server_protocol.h +++ b/include/wine/server_protocol.h @@ -14,6 +14,7 @@ #include #include +#include typedef unsigned int obj_handle_t; typedef unsigned int user_handle_t; @@ -470,7 +471,8 @@ enum apc_type APC_MAP_VIEW, APC_UNMAP_VIEW, APC_CREATE_THREAD, - APC_DUP_HANDLE + APC_DUP_HANDLE, + APC_PROCESS_SET_INFO, }; typedef struct @@ -579,6 +581,11 @@ typedef union unsigned int attributes; unsigned int options; } dup_handle; + struct + { + enum apc_type type; + PROCESSINFOCLASS class; + } process_set_info; } apc_call_t; typedef union @@ -677,6 +684,11 @@ typedef union enum apc_type type; unsigned int status; } break_process; + struct + { + enum apc_type type; + unsigned int status; + } process_set_info; } apc_result_t; enum irp_type @@ -815,6 +827,76 @@ typedef struct lparam_t info; } cursor_pos_t; +struct cpu_topology_override +{ + unsigned int cpu_count; + unsigned char host_cpu_id[64]; +}; + +struct shared_cursor +{ + int x; + int y; + unsigned int last_change; + rectangle_t clip; +}; + +struct ws_watch_entry +{ + client_ptr_t va; + client_ptr_t pc; + thread_id_t tid; + int __pad; +}; + +struct ws_watch_data +{ + data_size_t len; + int __pad; + struct ws_watch_entry entries[1]; +}; + +struct desktop_shared_memory +{ + unsigned int seq; + struct shared_cursor cursor; + unsigned char keystate[256]; + thread_id_t foreground_tid; +}; + +struct queue_shared_memory +{ + unsigned int seq; + int created; + unsigned int wake_bits; + unsigned int changed_bits; + unsigned int wake_mask; + unsigned int changed_mask; + thread_id_t input_tid; +}; + +struct input_shared_memory +{ + unsigned int seq; + int created; + thread_id_t tid; + user_handle_t focus; + user_handle_t capture; + user_handle_t active; + user_handle_t menu_owner; + user_handle_t move_size; + user_handle_t caret; + user_handle_t cursor; + rectangle_t caret_rect; + int cursor_count; + unsigned char keystate[256]; + int keystate_lock; +}; + + +#define SEQUENCE_MASK_BITS 4 +#define SEQUENCE_MASK ((1UL << SEQUENCE_MASK_BITS) - 1) + @@ -907,11 +989,15 @@ struct init_process_done_request client_ptr_t teb; client_ptr_t peb; client_ptr_t ldt_copy; + client_ptr_t kernel_stack; + unsigned int kernel_stack_size; + char __pad_52[4]; }; struct init_process_done_reply { struct reply_header __header; client_ptr_t entry; + /* VARARG(cpu_override,cpu_topology_override); */ int suspend; char __pad_20[4]; }; @@ -926,6 +1012,8 @@ struct init_first_thread_request int debug_level; int reply_fd; int wait_fd; + char nice_limit; + char __pad_33[7]; }; struct init_first_thread_reply { @@ -948,6 +1036,9 @@ struct init_thread_request int wait_fd; client_ptr_t teb; client_ptr_t entry; + client_ptr_t kernel_stack; + unsigned int kernel_stack_size; + char __pad_52[4]; }; struct init_thread_reply { @@ -1748,14 +1839,16 @@ struct recv_socket_request struct request_header __header; int oob; async_data_t async; - unsigned int status; - unsigned int total; + int force_async; + char __pad_60[4]; }; struct recv_socket_reply { struct reply_header __header; obj_handle_t wait; unsigned int options; + int nonblocking; + char __pad_20[4]; }; @@ -2139,7 +2232,9 @@ struct read_process_memory_request struct read_process_memory_reply { struct reply_header __header; + int unix_pid; /* VARARG(data,bytes); */ + char __pad_12[4]; }; @@ -2686,10 +2781,10 @@ struct send_hardware_message_reply int prev_y; int new_x; int new_y; - /* VARARG(keystate,bytes); */ char __pad_28[4]; }; #define SEND_HWMSG_INJECTED 0x01 +#define SEND_HWMSG_RAWINPUT 0x02 @@ -2715,9 +2810,9 @@ struct get_message_reply int x; int y; unsigned int time; - unsigned int active_hooks; data_size_t total; /* VARARG(data,message_data); */ + char __pad_52[4]; }; @@ -2889,6 +2984,23 @@ struct get_async_result_reply +struct set_async_direct_result_request +{ + struct request_header __header; + obj_handle_t handle; + apc_param_t information; + unsigned int status; + char __pad_28[4]; +}; +struct set_async_direct_result_reply +{ + struct reply_header __header; + obj_handle_t handle; + char __pad_12[4]; +}; + + + struct read_request { struct request_header __header; @@ -3769,14 +3881,10 @@ struct get_thread_input_reply user_handle_t focus; user_handle_t capture; user_handle_t active; - user_handle_t foreground; user_handle_t menu_owner; user_handle_t move_size; user_handle_t caret; - user_handle_t cursor; - int show_count; rectangle_t rect; - char __pad_60[4]; }; @@ -3934,6 +4042,20 @@ enum caret_state +struct get_active_hooks_request +{ + struct request_header __header; + char __pad_12[4]; +}; +struct get_active_hooks_reply +{ + struct reply_header __header; + unsigned int active_hooks; + char __pad_12[4]; +}; + + + struct set_hook_request { struct request_header __header; @@ -4985,6 +5107,8 @@ struct remove_completion_request { struct request_header __header; obj_handle_t handle; + int waited; + char __pad_20[4]; }; struct remove_completion_reply { @@ -5388,7 +5512,6 @@ struct resume_process_reply }; - struct get_next_thread_request { struct request_header __header; @@ -5405,6 +5528,200 @@ struct get_next_thread_reply char __pad_12[4]; }; +enum esync_type +{ + ESYNC_SEMAPHORE = 1, + ESYNC_AUTO_EVENT, + ESYNC_MANUAL_EVENT, + ESYNC_MUTEX, + ESYNC_AUTO_SERVER, + ESYNC_MANUAL_SERVER, + ESYNC_QUEUE, +}; + + +struct create_esync_request +{ + struct request_header __header; + unsigned int access; + int initval; + int type; + int max; + /* VARARG(objattr,object_attributes); */ + char __pad_28[4]; +}; +struct create_esync_reply +{ + struct reply_header __header; + obj_handle_t handle; + int type; + unsigned int shm_idx; + char __pad_20[4]; +}; + +struct open_esync_request +{ + struct request_header __header; + unsigned int access; + unsigned int attributes; + obj_handle_t rootdir; + int type; + /* VARARG(name,unicode_str); */ + char __pad_28[4]; +}; +struct open_esync_reply +{ + struct reply_header __header; + obj_handle_t handle; + int type; + unsigned int shm_idx; + char __pad_20[4]; +}; + + +struct get_esync_fd_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct get_esync_fd_reply +{ + struct reply_header __header; + int type; + unsigned int shm_idx; +}; + + +struct esync_msgwait_request +{ + struct request_header __header; + int in_msgwait; +}; +struct esync_msgwait_reply +{ + struct reply_header __header; +}; + + +struct get_esync_apc_fd_request +{ + struct request_header __header; + char __pad_12[4]; +}; +struct get_esync_apc_fd_reply +{ + struct reply_header __header; +}; + +enum fsync_type +{ + FSYNC_SEMAPHORE = 1, + FSYNC_AUTO_EVENT, + FSYNC_MANUAL_EVENT, + FSYNC_MUTEX, + FSYNC_AUTO_SERVER, + FSYNC_MANUAL_SERVER, + FSYNC_QUEUE, +}; + + +struct create_fsync_request +{ + struct request_header __header; + unsigned int access; + int low; + int high; + int type; + /* VARARG(objattr,object_attributes); */ + char __pad_28[4]; +}; +struct create_fsync_reply +{ + struct reply_header __header; + obj_handle_t handle; + int type; + unsigned int shm_idx; + char __pad_20[4]; +}; + + +struct open_fsync_request +{ + struct request_header __header; + unsigned int access; + unsigned int attributes; + obj_handle_t rootdir; + int type; + /* VARARG(name,unicode_str); */ + char __pad_28[4]; +}; +struct open_fsync_reply +{ + struct reply_header __header; + obj_handle_t handle; + int type; + unsigned int shm_idx; + char __pad_20[4]; +}; + + +struct get_fsync_idx_request +{ + struct request_header __header; + obj_handle_t handle; +}; +struct get_fsync_idx_reply +{ + struct reply_header __header; + int type; + unsigned int shm_idx; +}; + +struct fsync_msgwait_request +{ + struct request_header __header; + int in_msgwait; +}; +struct fsync_msgwait_reply +{ + struct reply_header __header; +}; + +struct get_fsync_apc_idx_request +{ + struct request_header __header; + char __pad_12[4]; +}; +struct get_fsync_apc_idx_reply +{ + struct reply_header __header; + unsigned int shm_idx; + char __pad_12[4]; +}; + + +struct init_working_set_watch_request +{ + struct request_header __header; + int fd; +}; +struct init_working_set_watch_reply +{ + struct reply_header __header; +}; + + +struct get_ws_watches_request +{ + struct request_header __header; + obj_handle_t process; +}; +struct get_ws_watches_reply +{ + struct reply_header __header; + /* VARARG(info,ws_watch_data); */ +}; + enum request { @@ -5531,6 +5848,7 @@ enum request REQ_register_async, REQ_cancel_async, REQ_get_async_result, + REQ_set_async_direct_result, REQ_read, REQ_write, REQ_ioctl, @@ -5591,6 +5909,7 @@ enum request REQ_set_capture_window, REQ_set_caret_window, REQ_set_caret_info, + REQ_get_active_hooks, REQ_set_hook, REQ_remove_hook, REQ_start_hook_chain, @@ -5682,6 +6001,18 @@ enum request REQ_suspend_process, REQ_resume_process, REQ_get_next_thread, + REQ_create_esync, + REQ_open_esync, + REQ_get_esync_fd, + REQ_esync_msgwait, + REQ_get_esync_apc_fd, + REQ_create_fsync, + REQ_open_fsync, + REQ_get_fsync_idx, + REQ_fsync_msgwait, + REQ_get_fsync_apc_idx, + REQ_init_working_set_watch, + REQ_get_ws_watches, REQ_NB_REQUESTS }; @@ -5812,6 +6143,7 @@ union generic_request struct register_async_request register_async_request; struct cancel_async_request cancel_async_request; struct get_async_result_request get_async_result_request; + struct set_async_direct_result_request set_async_direct_result_request; struct read_request read_request; struct write_request write_request; struct ioctl_request ioctl_request; @@ -5872,6 +6204,7 @@ union generic_request struct set_capture_window_request set_capture_window_request; struct set_caret_window_request set_caret_window_request; struct set_caret_info_request set_caret_info_request; + struct get_active_hooks_request get_active_hooks_request; struct set_hook_request set_hook_request; struct remove_hook_request remove_hook_request; struct start_hook_chain_request start_hook_chain_request; @@ -5963,6 +6296,18 @@ 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 create_esync_request create_esync_request; + struct open_esync_request open_esync_request; + struct get_esync_fd_request get_esync_fd_request; + struct esync_msgwait_request esync_msgwait_request; + struct get_esync_apc_fd_request get_esync_apc_fd_request; + struct create_fsync_request create_fsync_request; + struct open_fsync_request open_fsync_request; + struct get_fsync_idx_request get_fsync_idx_request; + struct fsync_msgwait_request fsync_msgwait_request; + struct get_fsync_apc_idx_request get_fsync_apc_idx_request; + struct init_working_set_watch_request init_working_set_watch_request; + struct get_ws_watches_request get_ws_watches_request; }; union generic_reply { @@ -6091,6 +6436,7 @@ union generic_reply struct register_async_reply register_async_reply; struct cancel_async_reply cancel_async_reply; struct get_async_result_reply get_async_result_reply; + struct set_async_direct_result_reply set_async_direct_result_reply; struct read_reply read_reply; struct write_reply write_reply; struct ioctl_reply ioctl_reply; @@ -6151,6 +6497,7 @@ union generic_reply struct set_capture_window_reply set_capture_window_reply; struct set_caret_window_reply set_caret_window_reply; struct set_caret_info_reply set_caret_info_reply; + struct get_active_hooks_reply get_active_hooks_reply; struct set_hook_reply set_hook_reply; struct remove_hook_reply remove_hook_reply; struct start_hook_chain_reply start_hook_chain_reply; @@ -6242,11 +6589,23 @@ 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 create_esync_reply create_esync_reply; + struct open_esync_reply open_esync_reply; + struct get_esync_fd_reply get_esync_fd_reply; + struct esync_msgwait_reply esync_msgwait_reply; + struct get_esync_apc_fd_reply get_esync_apc_fd_reply; + struct create_fsync_reply create_fsync_reply; + struct open_fsync_reply open_fsync_reply; + struct get_fsync_idx_reply get_fsync_idx_reply; + struct fsync_msgwait_reply fsync_msgwait_reply; + struct get_fsync_apc_idx_reply get_fsync_apc_idx_reply; + struct init_working_set_watch_reply init_working_set_watch_reply; + struct get_ws_watches_reply get_ws_watches_reply; }; /* ### protocol_version begin ### */ -#define SERVER_PROTOCOL_VERSION 739 +#define SERVER_PROTOCOL_VERSION 740 /* ### protocol_version end ### */ diff --git a/loader/wine.inf.in b/loader/wine.inf.in index fe8370742b2..a08568eb9a3 100644 --- a/loader/wine.inf.in +++ b/loader/wine.inf.in @@ -5930,6 +5930,7 @@ HKCU,Software\Wine\AppDefaults\gotg.exe\DllOverrides,"atiadlxx",,"builtin" HKCU,Software\Wine\AppDefaults\RelicCardinal.exe\DllOverrides,"atiadlxx",,"builtin" HKCU,Software\Wine\AppDefaults\msedgewebview2.exe,"Version",,"win81" HKCU,Software\Wine\AppDefaults\Avengers.exe\DllOverrides,"atiadlxx",,"builtin" +HKCU,Software\Wine\AppDefaults\starwarssquadrons.exe\DllOverrides,"atiadlxx",,"builtin" ;;App-specific overrides to limit the number of resolutions HKCU,Software\Wine\AppDefaults\DarkSoulsIII.exe\X11 Driver,"LimitNumberOfResolutions",0x2,"32" HKCU,Software\Wine\AppDefaults\sekiro.exe\X11 Driver,"LimitNumberOfResolutions",0x2,"32" diff --git a/programs/belauncher/Makefile.in b/programs/belauncher/Makefile.in new file mode 100644 index 00000000000..f2dc59b07ce --- /dev/null +++ b/programs/belauncher/Makefile.in @@ -0,0 +1,7 @@ +MODULE = belauncher.exe +IMPORTS = + +EXTRADLLFLAGS = -mwindows -municode + +C_SRCS = \ + main.c \ diff --git a/programs/belauncher/main.c b/programs/belauncher/main.c new file mode 100644 index 00000000000..86ec46ecee4 --- /dev/null +++ b/programs/belauncher/main.c @@ -0,0 +1,117 @@ +#define WIN32_LEAN_AND_MEAN +#include + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(belauncher); + +int WINAPI wWinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPWSTR cmdline, int cmdshow) +{ + char *configs, *config, *arch_32_exe = NULL, *arch_64_exe = NULL, *game_exe, *be_arg = NULL; + LARGE_INTEGER launcher_cfg_size; + unsigned char battleye_status; + int game_exe_len, arg_len; + PROCESS_INFORMATION pi; + HANDLE launcher_cfg; + LPWSTR launch_cmd; + STARTUPINFOW si = {0}; + DWORD size; + BOOL wow64; + + battleye_status = 0x3; /* Starting */ + _write(1, &battleye_status, 1); + + launcher_cfg = CreateFileW(L"Battleye\\BELauncher.ini", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (launcher_cfg == INVALID_HANDLE_VALUE) + goto start_failed; + + if(!GetFileSizeEx(launcher_cfg, &launcher_cfg_size) || launcher_cfg_size.u.HighPart) + { + CloseHandle(launcher_cfg); + goto start_failed; + } + + configs = HeapAlloc( GetProcessHeap(), 0, launcher_cfg_size.u.LowPart); + + if (!ReadFile(launcher_cfg, configs, launcher_cfg_size.u.LowPart, &size, NULL) || size != launcher_cfg_size.u.LowPart) + { + CloseHandle(launcher_cfg); + HeapFree( GetProcessHeap(), 0, configs ); + goto start_failed; + } + + CloseHandle(launcher_cfg); + + config = configs; + do + { + if (!strncmp(config, "32BitExe=", 9)) + arch_32_exe = config + 9; + + if (!strncmp(config, "64BitExe=", 9)) + arch_64_exe = config + 9; + + if (!strncmp(config, "BEArg=", 6)) + be_arg = config + 6; + } + while ((config = strchr(config, '\n')) && *(config++)); + + if (arch_64_exe && (sizeof(void *) == 8 || (IsWow64Process(GetCurrentProcess(), &wow64) && wow64))) + game_exe = arch_64_exe; + else if (arch_32_exe) + game_exe = arch_32_exe; + else + { + HeapFree( GetProcessHeap(), 0, configs ); + WINE_WARN("Failed to find game executable name from BattlEye config.\n"); + goto start_failed; + } + + if (strchr(game_exe, '\r')) + *(strchr(game_exe, '\r')) = 0; + if (strchr(game_exe, '\n')) + *(strchr(game_exe, '\n')) = 0; + game_exe_len = MultiByteToWideChar(CP_ACP, 0, game_exe, -1, NULL, 0) - 1; + + if (!be_arg) arg_len = 0; + else + { + if (strchr(be_arg, '\r')) + *(strchr(be_arg, '\r')) = 0; + if (strchr(be_arg, '\n')) + *(strchr(be_arg, '\n')) = 0; + arg_len = MultiByteToWideChar(CP_ACP, 0, be_arg, -1, NULL, 0) - 1; + } + + WINE_TRACE("Launching game executable %s for BattlEye.\n", game_exe); + battleye_status = 0x9; /* Launching Game */ + _write(1, &battleye_status, 1); + + launch_cmd = HeapAlloc(GetProcessHeap(), 0, (game_exe_len + 1 + wcslen(cmdline) + 1 + arg_len + 1) * sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, game_exe, -1, launch_cmd, game_exe_len + 1); + launch_cmd[game_exe_len] = ' '; + + wcscpy(launch_cmd + game_exe_len + 1, cmdline); + launch_cmd[game_exe_len + 1 + wcslen(cmdline)] = ' '; + + MultiByteToWideChar(CP_ACP, 0, be_arg, -1, launch_cmd + game_exe_len + 1 + wcslen(cmdline) + 1, arg_len + 1); + + if (!CreateProcessW(NULL, launch_cmd, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi)) + { + battleye_status = 0xA; /* Launch Failed */ + _write(1, &battleye_status, 1); + + HeapFree( GetProcessHeap(), 0, launch_cmd ); + return GetLastError(); + } + HeapFree( GetProcessHeap(), 0, launch_cmd ); + + WaitForSingleObject(pi.hProcess, INFINITE); + CloseHandle(pi.hProcess); + return 0; + +start_failed: + battleye_status = 0x4; /* Start Failed */ + _write(1, &battleye_status, 1); + return 0; +} diff --git a/programs/wineboot/wineboot.c b/programs/wineboot/wineboot.c index 16b44ca28ab..012155fccba 100644 --- a/programs/wineboot/wineboot.c +++ b/programs/wineboot/wineboot.c @@ -83,6 +83,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(wineboot); +#define TICKSPERSEC 10000000 + extern BOOL shutdown_close_windows( BOOL force ); extern BOOL shutdown_all_desktops( BOOL force ); extern void kill_processes( BOOL kill_desktop ); @@ -242,15 +244,173 @@ static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) TRACE("XSAVE feature 2 %#x, %#x, %#x, %#x.\n", regs[0], regs[1], regs[2], regs[3]); } +static UINT64 read_tsc_frequency( BOOL has_rdtscp ) +{ + UINT64 freq = 0; + +/* FIXME: Intel provides TSC freq in some CPUID but it's been slightly broken, + fix it properly and test it on real Intel hardware */ + +#if 0 + int regs[4], cpuid_level, tmp; + UINT64 denom, numer; + + __cpuid( regs, 0 ); + tmp = regs[2]; + regs[2] = regs[3]; + regs[3] = tmp; + + /* only available on some intel CPUs */ + if (memcmp( regs + 1, "GenuineIntel", 12 )) freq = 0; + else if ((cpuid_level = regs[0]) < 0x15) freq = 0; + else + { + __cpuid( regs, 0x15 ); + if (!(denom = regs[0]) || !(numer = regs[1])) freq = 0; + else + { + if ((freq = regs[2])) freq = freq * numer / denom; + else if (cpuid_level >= 0x16) + { + __cpuid( regs, 0x16 ); /* eax is base freq in MHz */ + freq = regs[0] * (UINT64)1000000; + } + else freq = 0; + } + + if (!freq) WARN( "Failed to read TSC frequency from CPUID, falling back to calibration.\n" ); + else TRACE( "TSC frequency read from CPUID, found %I64u Hz\n", freq ); + } +#endif + + if (freq == 0) + { + LONGLONG time0, time1, tsc0, tsc1, tsc2, tsc3, freq0, freq1, error; + unsigned int aux; + UINT retries = 50; + int regs[4]; + + do + { + if (has_rdtscp) + { + tsc0 = __rdtscp( &aux ); + time0 = RtlGetSystemTimePrecise(); + tsc1 = __rdtscp( &aux ); + Sleep( 1 ); + tsc2 = __rdtscp( &aux ); + time1 = RtlGetSystemTimePrecise(); + tsc3 = __rdtscp( &aux ); + } + else + { + tsc0 = __rdtsc(); __cpuid( regs, 0 ); + time0 = RtlGetSystemTimePrecise(); + tsc1 = __rdtsc(); __cpuid( regs, 0 ); + Sleep(1); + tsc2 = __rdtsc(); __cpuid( regs, 0 ); + time1 = RtlGetSystemTimePrecise(); + tsc3 = __rdtsc(); __cpuid( regs, 0 ); + } + + freq0 = (tsc2 - tsc0) * 10000000 / (time1 - time0); + freq1 = (tsc3 - tsc1) * 10000000 / (time1 - time0); + error = llabs( (freq1 - freq0) * 1000000 / min( freq1, freq0 ) ); + } + while (error > 100 && --retries); + + if (!retries) WARN( "TSC frequency calibration failed, unstable TSC?\n" ); + else + { + freq = (freq0 + freq1) / 2; + TRACE( "TSC frequency calibration complete, found %I64u Hz\n", freq ); + } + } + + return freq; +} + +static BOOL is_tsc_trusted_by_the_kernel(void) +{ + char buf[4] = {}; + DWORD num_read; + HANDLE handle; + BOOL ret = TRUE; + + handle = CreateFileA( "\\??\\unix\\sys\\bus\\clocksource\\devices\\clocksource0\\current_clocksource", + GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 ); + if (handle == INVALID_HANDLE_VALUE) return TRUE; + + if (ReadFile( handle, buf, sizeof(buf) - 1, &num_read, NULL ) && strcmp( "tsc", buf )) + ret = FALSE; + + CloseHandle( handle ); + return ret; +} + +static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) +{ + BOOL has_rdtscp = FALSE; + int regs[4]; + + data->QpcBypassEnabled = 0; + data->QpcFrequency = TICKSPERSEC; + data->QpcShift = 0; + data->QpcBias = 0; + *tsc_frequency = 0; + + if (!is_tsc_trusted_by_the_kernel()) + { + WARN( "Failed to compute TSC frequency, not trusted by the kernel.\n" ); + return; + } + + if (!data->ProcessorFeatures[PF_RDTSC_INSTRUCTION_AVAILABLE]) + { + WARN( "Failed to compute TSC frequency, RDTSC instruction not supported.\n" ); + return; + } + + __cpuid( regs, 0x80000000 ); + if (regs[0] < 0x80000007) + { + WARN( "Failed to compute TSC frequency, unable to check invariant TSC.\n" ); + return; + } + + /* check for invariant tsc bit */ + __cpuid( regs, 0x80000007 ); + if (!(regs[3] & (1 << 8))) + { + WARN( "Failed to compute TSC frequency, no invariant TSC.\n" ); + return; + } + + /* check for rdtscp support bit */ + __cpuid( regs, 0x80000001 ); + if ((regs[3] & (1 << 27))) has_rdtscp = TRUE; + + *tsc_frequency = read_tsc_frequency( has_rdtscp ); +} + #else static void initialize_xstate_features(struct _KUSER_SHARED_DATA *data) { } +static void initialize_qpc_features( struct _KUSER_SHARED_DATA *data, UINT64 *tsc_frequency ) +{ + data->QpcBypassEnabled = 0; + data->QpcFrequency = TICKSPERSEC; + data->QpcShift = 0; + data->QpcBias = 0; + *tsc_frequency = 0; +} + #endif -static void create_user_shared_data(void) +static void create_user_shared_data( UINT64 *tsc_frequency ) { struct _KUSER_SHARED_DATA *data; RTL_OSVERSIONINFOEXW version; @@ -337,6 +497,7 @@ static void create_user_shared_data(void) data->ActiveGroupCount = 1; initialize_xstate_features( data ); + initialize_qpc_features( data, tsc_frequency ); UnmapViewOfFile( data ); } @@ -648,7 +809,7 @@ static void create_bios_key( HKEY system_key ) } /* create the volatile hardware registry keys */ -static void create_hardware_registry_keys(void) +static void create_hardware_registry_keys( UINT64 tsc_frequency ) { unsigned int i; HKEY hkey, system_key, cpu_key, fpu_key; @@ -723,12 +884,15 @@ static void create_hardware_registry_keys(void) if (!RegCreateKeyExW( cpu_key, numW, 0, NULL, REG_OPTION_VOLATILE, KEY_ALL_ACCESS, NULL, &hkey, NULL )) { + DWORD tsc_freq_mhz = (DWORD)(tsc_frequency / 1000000ull); /* Hz -> Mhz */ + if (!tsc_freq_mhz) tsc_freq_mhz = power_info[i].MaxMhz; + RegSetValueExW( hkey, L"FeatureSet", 0, REG_DWORD, (BYTE *)&sci.ProcessorFeatureBits, sizeof(DWORD) ); set_reg_value( hkey, L"Identifier", id ); /* TODO: report ARM properly */ set_reg_value( hkey, L"ProcessorNameString", namestr ); set_reg_value( hkey, L"VendorIdentifier", vendorid ); - RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&power_info[i].MaxMhz, sizeof(DWORD) ); + RegSetValueExW( hkey, L"~MHz", 0, REG_DWORD, (BYTE *)&tsc_freq_mhz, sizeof(DWORD) ); RegCloseKey( hkey ); } if (sci.ProcessorArchitecture != PROCESSOR_ARCHITECTURE_ARM && @@ -1659,6 +1823,7 @@ int __cdecl main( int argc, char *argv[] ) BOOL end_session, force, init, kill, restart, shutdown, update; HANDLE event; OBJECT_ATTRIBUTES attr; + UINT64 tsc_frequency = 0; UNICODE_STRING nameW; BOOL is_wow64; @@ -1745,8 +1910,8 @@ int __cdecl main( int argc, char *argv[] ) ResetEvent( event ); /* in case this is a restart */ - create_user_shared_data(); - create_hardware_registry_keys(); + create_user_shared_data( &tsc_frequency ); + create_hardware_registry_keys( tsc_frequency ); create_dynamic_registry_keys(); create_environment_registry_keys(); create_computer_name_keys(); diff --git a/server/mapping.c b/server/mapping.c index ecbfbee7ea1..4514a877177 100644 --- a/server/mapping.c +++ b/server/mapping.c @@ -268,6 +268,7 @@ int grow_file( int unix_fd, file_pos_t new_size ) return 0; } +#ifndef HAVE_MEMFD_CREATE /* simplified version of mkstemps() */ static int make_temp_file( char name[16] ) { @@ -301,10 +302,23 @@ static int check_current_dir_for_exec(void) unlink( tmpfn ); return (ret != MAP_FAILED); } +#endif /* create a temp file for anonymous mappings */ static int create_temp_file( file_pos_t size ) { +#ifdef HAVE_MEMFD_CREATE + int fd = memfd_create( "wine-mapping", MFD_ALLOW_SEALING ); + if (fd != -1) + { + if (!grow_file( fd, size )) + { + close( fd ); + fd = -1; + } + } + else file_set_error(); +#else static int temp_dir_fd = -1; char tmpfn[16]; int fd; @@ -337,6 +351,7 @@ static int create_temp_file( file_pos_t size ) else file_set_error(); if (temp_dir_fd != server_dir_fd) fchdir( server_dir_fd ); +#endif return fd; } @@ -1103,6 +1118,10 @@ int get_page_size(void) return page_mask + 1; } +#ifndef F_SEAL_FUTURE_WRITE +#define F_SEAL_FUTURE_WRITE 0x0010 /* prevent future writes while mapped */ +#endif + struct object *create_user_data_mapping( struct object *root, const struct unicode_str *name, unsigned int attr, const struct security_descriptor *sd ) { @@ -1112,6 +1131,7 @@ struct object *create_user_data_mapping( struct object *root, const struct unico if (!(mapping = create_mapping( root, name, attr, sizeof(KSHARED_USER_DATA), SEC_COMMIT, 0, FILE_READ_DATA | FILE_WRITE_DATA, sd ))) return NULL; ptr = mmap( NULL, mapping->size, PROT_WRITE, MAP_SHARED, get_unix_fd( mapping->fd ), 0 ); + if (ptr != MAP_FAILED) { user_shared_data = ptr; @@ -1123,11 +1143,15 @@ struct object *create_user_data_mapping( struct object *root, const struct unico struct object *create_shared_mapping( struct object *root, const struct unicode_str *name, mem_size_t size, const struct security_descriptor *sd, void **ptr ) { + static int seals = F_SEAL_FUTURE_WRITE | F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_SEAL; struct mapping *mapping; if (!(mapping = create_mapping( root, name, OBJ_OPENIF, size, SEC_COMMIT, 0, FILE_READ_DATA | FILE_WRITE_DATA, sd ))) return NULL; *ptr = mmap( NULL, mapping->size, PROT_WRITE, MAP_SHARED, get_unix_fd( mapping->fd ), 0 ); + + fcntl( get_unix_fd( mapping->fd ), F_ADD_SEALS, seals ); + if (*ptr == MAP_FAILED) { release_object( &mapping->obj ); diff --git a/server/process.c b/server/process.c index 8203eead9d4..1f9ec4eb619 100644 --- a/server/process.c +++ b/server/process.c @@ -1462,6 +1462,24 @@ DECL_HANDLER(init_process_done) const pe_image_info_t *image_info; const struct cpu_topology_override *cpu_override = get_req_data(); unsigned int have_cpu_override = get_req_data_size() / sizeof(*cpu_override); + unsigned int i; + + if (have_cpu_override) + { + if (cpu_override->cpu_count > ARRAY_SIZE(process->wine_cpu_id_from_host)) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } + for (i = 0; i < cpu_override->cpu_count; ++i) + { + if (cpu_override->host_cpu_id[i] >= ARRAY_SIZE(process->wine_cpu_id_from_host)) + { + set_error( STATUS_INVALID_PARAMETER ); + return; + } + } + } if (is_process_init_done(process)) { @@ -1493,7 +1511,12 @@ DECL_HANDLER(init_process_done) reply->suspend = (current->suspend || process->suspend); if (have_cpu_override) + { process->cpu_override = *cpu_override; + memset( process->wine_cpu_id_from_host, 0, sizeof(process->wine_cpu_id_from_host) ); + for (i = 0; i < process->cpu_override.cpu_count; ++i) + process->wine_cpu_id_from_host[process->cpu_override.host_cpu_id[i]] = i; + } } /* open a handle to a process */ diff --git a/server/process.h b/server/process.h index 404bbb12550..60a80be1fc1 100644 --- a/server/process.h +++ b/server/process.h @@ -94,6 +94,7 @@ struct process int esync_fd; /* esync file descriptor (signaled on exit) */ unsigned int fsync_idx; struct cpu_topology_override cpu_override; /* Overridden CPUs to host CPUs mapping. */ + unsigned char wine_cpu_id_from_host[64]; /* Host to overridden CPU mapping. */ }; /* process functions */ diff --git a/server/protocol.def b/server/protocol.def index fbf8a017cd0..4dca9ee3f45 100644 --- a/server/protocol.def +++ b/server/protocol.def @@ -2821,6 +2821,7 @@ enum coords_relative /* Set the current thread active window */ @REQ(set_active_window) user_handle_t handle; /* handle to the active window */ + unsigned int internal_msg; /* set active window internal message */ @REPLY user_handle_t previous; /* handle to the previous active window */ @END diff --git a/server/queue.c b/server/queue.c index 15c46aea3ab..b07dceb2043 100644 --- a/server/queue.c +++ b/server/queue.c @@ -3503,7 +3503,11 @@ DECL_HANDLER(set_focus_window) /* set the current thread active window */ DECL_HANDLER(set_active_window) { + struct message *msg, *next; struct msg_queue *queue = get_current_queue(); + struct desktop *desktop; + + if (!(desktop = get_thread_desktop( current, 0 ))) return; reply->previous = 0; if (queue && check_queue_input_window( queue, req->handle )) @@ -3514,9 +3518,17 @@ DECL_HANDLER(set_active_window) SHARED_WRITE_BEGIN( &queue->input->shared->seq ); queue->input->shared->active = get_user_full_handle( req->handle ); SHARED_WRITE_END( &queue->input->shared->seq ); + + if (desktop->foreground_input == queue->input && req->handle != reply->previous) + { + LIST_FOR_EACH_ENTRY_SAFE( msg, next, &queue->msg_list[POST_MESSAGE], struct message, entry ) + if (msg->msg == req->internal_msg) remove_queue_message( queue, msg, POST_MESSAGE ); + } } else set_error( STATUS_INVALID_HANDLE ); } + + release_object( desktop ); } diff --git a/server/request.c b/server/request.c index 6e1820313d1..a1d646f9643 100644 --- a/server/request.c +++ b/server/request.c @@ -519,6 +519,46 @@ int send_client_fd( struct process *process, int fd, obj_handle_t handle ) return -1; } +#ifndef __NR_clock_gettime64 +#define __NR_clock_gettime64 403 +#endif + +struct timespec64 +{ + long long tv_sec; + long long tv_nsec; +}; + +static inline int do_clock_gettime( clockid_t clock_id, ULONGLONG *ticks ) +{ + static int clock_gettime64_supported = -1; + struct timespec64 ts64; + struct timespec ts; + int ret; + + if (clock_gettime64_supported < 0) + { + if (!syscall( __NR_clock_gettime64, clock_id, &ts64 )) + { + clock_gettime64_supported = 1; + *ticks = ts64.tv_sec * (ULONGLONG)TICKS_PER_SEC + ts64.tv_nsec / 100; + return 0; + } + clock_gettime64_supported = 0; + } + + if (clock_gettime64_supported) + { + if (!(ret = syscall( __NR_clock_gettime64, clock_id, &ts64 ))) + *ticks = ts64.tv_sec * (ULONGLONG)TICKS_PER_SEC + ts64.tv_nsec / 100; + return ret; + } + + if (!(ret = clock_gettime( clock_id, &ts ))) + *ticks = ts.tv_sec * (ULONGLONG)TICKS_PER_SEC + ts.tv_nsec / 100; + return ret; +} + /* return a monotonic time counter */ timeout_t monotonic_counter(void) { @@ -532,13 +572,13 @@ timeout_t monotonic_counter(void) #endif return mach_absolute_time() * timebase.numer / timebase.denom / 100; #elif defined(HAVE_CLOCK_GETTIME) - struct timespec ts; + ULONGLONG ticks; #if 0 - if (!clock_gettime( CLOCK_MONOTONIC_RAW, &ts )) - return (timeout_t)ts.tv_sec * TICKS_PER_SEC + ts.tv_nsec / 100; + if (!do_clock_gettime( CLOCK_MONOTONIC_RAW, &ticks )) + return ticks; #endif - if (!clock_gettime( CLOCK_MONOTONIC, &ts )) - return (timeout_t)ts.tv_sec * TICKS_PER_SEC + ts.tv_nsec / 100; + if (!do_clock_gettime( CLOCK_MONOTONIC, &ticks )) + return ticks; #endif return current_time - server_start_time; } diff --git a/server/request.h b/server/request.h index 3c455799d54..8c5da1b3c48 100644 --- a/server/request.h +++ b/server/request.h @@ -242,6 +242,7 @@ DECL_HANDLER(set_serial_info); DECL_HANDLER(register_async); DECL_HANDLER(cancel_async); DECL_HANDLER(get_async_result); +DECL_HANDLER(set_async_direct_result); DECL_HANDLER(read); DECL_HANDLER(write); DECL_HANDLER(ioctl); @@ -302,6 +303,7 @@ DECL_HANDLER(set_active_window); DECL_HANDLER(set_capture_window); DECL_HANDLER(set_caret_window); DECL_HANDLER(set_caret_info); +DECL_HANDLER(get_active_hooks); DECL_HANDLER(set_hook); DECL_HANDLER(remove_hook); DECL_HANDLER(start_hook_chain); @@ -393,6 +395,18 @@ DECL_HANDLER(terminate_job); DECL_HANDLER(suspend_process); DECL_HANDLER(resume_process); DECL_HANDLER(get_next_thread); +DECL_HANDLER(create_esync); +DECL_HANDLER(open_esync); +DECL_HANDLER(get_esync_fd); +DECL_HANDLER(esync_msgwait); +DECL_HANDLER(get_esync_apc_fd); +DECL_HANDLER(create_fsync); +DECL_HANDLER(open_fsync); +DECL_HANDLER(get_fsync_idx); +DECL_HANDLER(fsync_msgwait); +DECL_HANDLER(get_fsync_apc_idx); +DECL_HANDLER(init_working_set_watch); +DECL_HANDLER(get_ws_watches); #ifdef WANT_REQUEST_HANDLERS @@ -522,6 +536,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_register_async, (req_handler)req_cancel_async, (req_handler)req_get_async_result, + (req_handler)req_set_async_direct_result, (req_handler)req_read, (req_handler)req_write, (req_handler)req_ioctl, @@ -582,6 +597,7 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_set_capture_window, (req_handler)req_set_caret_window, (req_handler)req_set_caret_info, + (req_handler)req_get_active_hooks, (req_handler)req_set_hook, (req_handler)req_remove_hook, (req_handler)req_start_hook_chain, @@ -673,6 +689,18 @@ 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_create_esync, + (req_handler)req_open_esync, + (req_handler)req_get_esync_fd, + (req_handler)req_esync_msgwait, + (req_handler)req_get_esync_apc_fd, + (req_handler)req_create_fsync, + (req_handler)req_open_fsync, + (req_handler)req_get_fsync_idx, + (req_handler)req_fsync_msgwait, + (req_handler)req_get_fsync_apc_idx, + (req_handler)req_init_working_set_watch, + (req_handler)req_get_ws_watches, }; C_ASSERT( sizeof(abstime_t) == 8 ); @@ -739,7 +767,9 @@ C_ASSERT( sizeof(struct get_startup_info_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct init_process_done_request, teb) == 16 ); C_ASSERT( FIELD_OFFSET(struct init_process_done_request, peb) == 24 ); C_ASSERT( FIELD_OFFSET(struct init_process_done_request, ldt_copy) == 32 ); -C_ASSERT( sizeof(struct init_process_done_request) == 40 ); +C_ASSERT( FIELD_OFFSET(struct init_process_done_request, kernel_stack) == 40 ); +C_ASSERT( FIELD_OFFSET(struct init_process_done_request, kernel_stack_size) == 48 ); +C_ASSERT( sizeof(struct init_process_done_request) == 56 ); C_ASSERT( FIELD_OFFSET(struct init_process_done_reply, entry) == 8 ); C_ASSERT( FIELD_OFFSET(struct init_process_done_reply, suspend) == 16 ); C_ASSERT( sizeof(struct init_process_done_reply) == 24 ); @@ -748,7 +778,8 @@ C_ASSERT( FIELD_OFFSET(struct init_first_thread_request, unix_tid) == 16 ); C_ASSERT( FIELD_OFFSET(struct init_first_thread_request, debug_level) == 20 ); C_ASSERT( FIELD_OFFSET(struct init_first_thread_request, reply_fd) == 24 ); C_ASSERT( FIELD_OFFSET(struct init_first_thread_request, wait_fd) == 28 ); -C_ASSERT( sizeof(struct init_first_thread_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct init_first_thread_request, nice_limit) == 32 ); +C_ASSERT( sizeof(struct init_first_thread_request) == 40 ); C_ASSERT( FIELD_OFFSET(struct init_first_thread_reply, pid) == 8 ); C_ASSERT( FIELD_OFFSET(struct init_first_thread_reply, tid) == 12 ); C_ASSERT( FIELD_OFFSET(struct init_first_thread_reply, server_start) == 16 ); @@ -760,7 +791,9 @@ C_ASSERT( FIELD_OFFSET(struct init_thread_request, reply_fd) == 16 ); C_ASSERT( FIELD_OFFSET(struct init_thread_request, wait_fd) == 20 ); C_ASSERT( FIELD_OFFSET(struct init_thread_request, teb) == 24 ); C_ASSERT( FIELD_OFFSET(struct init_thread_request, entry) == 32 ); -C_ASSERT( sizeof(struct init_thread_request) == 40 ); +C_ASSERT( FIELD_OFFSET(struct init_thread_request, kernel_stack) == 40 ); +C_ASSERT( FIELD_OFFSET(struct init_thread_request, kernel_stack_size) == 48 ); +C_ASSERT( sizeof(struct init_thread_request) == 56 ); C_ASSERT( FIELD_OFFSET(struct init_thread_reply, suspend) == 8 ); C_ASSERT( sizeof(struct init_thread_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct terminate_process_request, handle) == 12 ); @@ -1042,12 +1075,12 @@ C_ASSERT( FIELD_OFFSET(struct unlock_file_request, count) == 24 ); C_ASSERT( sizeof(struct unlock_file_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_request, oob) == 12 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_request, async) == 16 ); -C_ASSERT( FIELD_OFFSET(struct recv_socket_request, status) == 56 ); -C_ASSERT( FIELD_OFFSET(struct recv_socket_request, total) == 60 ); +C_ASSERT( FIELD_OFFSET(struct recv_socket_request, force_async) == 56 ); C_ASSERT( sizeof(struct recv_socket_request) == 64 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, wait) == 8 ); C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, options) == 12 ); -C_ASSERT( sizeof(struct recv_socket_reply) == 16 ); +C_ASSERT( FIELD_OFFSET(struct recv_socket_reply, nonblocking) == 16 ); +C_ASSERT( sizeof(struct recv_socket_reply) == 24 ); C_ASSERT( FIELD_OFFSET(struct send_socket_request, async) == 16 ); C_ASSERT( FIELD_OFFSET(struct send_socket_request, status) == 56 ); C_ASSERT( FIELD_OFFSET(struct send_socket_request, total) == 60 ); @@ -1162,7 +1195,8 @@ C_ASSERT( sizeof(struct set_debug_obj_info_request) == 24 ); C_ASSERT( FIELD_OFFSET(struct read_process_memory_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct read_process_memory_request, addr) == 16 ); C_ASSERT( sizeof(struct read_process_memory_request) == 24 ); -C_ASSERT( sizeof(struct read_process_memory_reply) == 8 ); +C_ASSERT( FIELD_OFFSET(struct read_process_memory_reply, unix_pid) == 8 ); +C_ASSERT( sizeof(struct read_process_memory_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct write_process_memory_request, handle) == 12 ); C_ASSERT( FIELD_OFFSET(struct write_process_memory_request, addr) == 16 ); C_ASSERT( sizeof(struct write_process_memory_request) == 24 ); @@ -1347,8 +1381,7 @@ C_ASSERT( FIELD_OFFSET(struct get_message_reply, type) == 32 ); C_ASSERT( FIELD_OFFSET(struct get_message_reply, x) == 36 ); C_ASSERT( FIELD_OFFSET(struct get_message_reply, y) == 40 ); C_ASSERT( FIELD_OFFSET(struct get_message_reply, time) == 44 ); -C_ASSERT( FIELD_OFFSET(struct get_message_reply, active_hooks) == 48 ); -C_ASSERT( FIELD_OFFSET(struct get_message_reply, total) == 52 ); +C_ASSERT( FIELD_OFFSET(struct get_message_reply, total) == 48 ); C_ASSERT( sizeof(struct get_message_reply) == 56 ); C_ASSERT( FIELD_OFFSET(struct reply_message_request, remove) == 12 ); C_ASSERT( FIELD_OFFSET(struct reply_message_request, result) == 16 ); @@ -1396,6 +1429,12 @@ C_ASSERT( sizeof(struct cancel_async_request) == 32 ); C_ASSERT( FIELD_OFFSET(struct get_async_result_request, user_arg) == 16 ); C_ASSERT( sizeof(struct get_async_result_request) == 24 ); C_ASSERT( sizeof(struct get_async_result_reply) == 8 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_request, handle) == 12 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_request, information) == 16 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_request, status) == 24 ); +C_ASSERT( sizeof(struct set_async_direct_result_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct set_async_direct_result_reply, handle) == 8 ); +C_ASSERT( sizeof(struct set_async_direct_result_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct read_request, async) == 16 ); C_ASSERT( FIELD_OFFSET(struct read_request, pos) == 56 ); C_ASSERT( sizeof(struct read_request) == 64 ); @@ -1703,14 +1742,11 @@ C_ASSERT( sizeof(struct get_thread_input_request) == 16 ); C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, focus) == 8 ); C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, capture) == 12 ); C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, active) == 16 ); -C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, foreground) == 20 ); -C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, menu_owner) == 24 ); -C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, move_size) == 28 ); -C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, caret) == 32 ); -C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, cursor) == 36 ); -C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, show_count) == 40 ); -C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, rect) == 44 ); -C_ASSERT( sizeof(struct get_thread_input_reply) == 64 ); +C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, menu_owner) == 20 ); +C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, move_size) == 24 ); +C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, caret) == 28 ); +C_ASSERT( FIELD_OFFSET(struct get_thread_input_reply, rect) == 32 ); +C_ASSERT( sizeof(struct get_thread_input_reply) == 48 ); C_ASSERT( sizeof(struct get_last_input_time_request) == 16 ); C_ASSERT( FIELD_OFFSET(struct get_last_input_time_reply, time) == 8 ); C_ASSERT( sizeof(struct get_last_input_time_reply) == 16 ); @@ -1762,6 +1798,9 @@ C_ASSERT( FIELD_OFFSET(struct set_caret_info_reply, old_rect) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_caret_info_reply, old_hide) == 28 ); C_ASSERT( FIELD_OFFSET(struct set_caret_info_reply, old_state) == 32 ); C_ASSERT( sizeof(struct set_caret_info_reply) == 40 ); +C_ASSERT( sizeof(struct get_active_hooks_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_active_hooks_reply, active_hooks) == 8 ); +C_ASSERT( sizeof(struct get_active_hooks_reply) == 16 ); C_ASSERT( FIELD_OFFSET(struct set_hook_request, id) == 12 ); C_ASSERT( FIELD_OFFSET(struct set_hook_request, pid) == 16 ); C_ASSERT( FIELD_OFFSET(struct set_hook_request, tid) == 20 ); @@ -2121,7 +2160,8 @@ C_ASSERT( FIELD_OFFSET(struct add_completion_request, information) == 32 ); C_ASSERT( FIELD_OFFSET(struct add_completion_request, status) == 40 ); C_ASSERT( sizeof(struct add_completion_request) == 48 ); C_ASSERT( FIELD_OFFSET(struct remove_completion_request, handle) == 12 ); -C_ASSERT( sizeof(struct remove_completion_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct remove_completion_request, waited) == 16 ); +C_ASSERT( sizeof(struct remove_completion_request) == 24 ); C_ASSERT( FIELD_OFFSET(struct remove_completion_reply, ckey) == 8 ); C_ASSERT( FIELD_OFFSET(struct remove_completion_reply, cvalue) == 16 ); C_ASSERT( FIELD_OFFSET(struct remove_completion_reply, information) == 24 ); @@ -2244,6 +2284,65 @@ 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( FIELD_OFFSET(struct create_esync_request, access) == 12 ); +C_ASSERT( FIELD_OFFSET(struct create_esync_request, initval) == 16 ); +C_ASSERT( FIELD_OFFSET(struct create_esync_request, type) == 20 ); +C_ASSERT( FIELD_OFFSET(struct create_esync_request, max) == 24 ); +C_ASSERT( sizeof(struct create_esync_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct create_esync_reply, handle) == 8 ); +C_ASSERT( FIELD_OFFSET(struct create_esync_reply, type) == 12 ); +C_ASSERT( FIELD_OFFSET(struct create_esync_reply, shm_idx) == 16 ); +C_ASSERT( sizeof(struct create_esync_reply) == 24 ); +C_ASSERT( FIELD_OFFSET(struct open_esync_request, access) == 12 ); +C_ASSERT( FIELD_OFFSET(struct open_esync_request, attributes) == 16 ); +C_ASSERT( FIELD_OFFSET(struct open_esync_request, rootdir) == 20 ); +C_ASSERT( FIELD_OFFSET(struct open_esync_request, type) == 24 ); +C_ASSERT( sizeof(struct open_esync_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct open_esync_reply, handle) == 8 ); +C_ASSERT( FIELD_OFFSET(struct open_esync_reply, type) == 12 ); +C_ASSERT( FIELD_OFFSET(struct open_esync_reply, shm_idx) == 16 ); +C_ASSERT( sizeof(struct open_esync_reply) == 24 ); +C_ASSERT( FIELD_OFFSET(struct get_esync_fd_request, handle) == 12 ); +C_ASSERT( sizeof(struct get_esync_fd_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_esync_fd_reply, type) == 8 ); +C_ASSERT( FIELD_OFFSET(struct get_esync_fd_reply, shm_idx) == 12 ); +C_ASSERT( sizeof(struct get_esync_fd_reply) == 16 ); +C_ASSERT( FIELD_OFFSET(struct esync_msgwait_request, in_msgwait) == 12 ); +C_ASSERT( sizeof(struct esync_msgwait_request) == 16 ); +C_ASSERT( sizeof(struct get_esync_apc_fd_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_request, access) == 12 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_request, low) == 16 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_request, high) == 20 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_request, type) == 24 ); +C_ASSERT( sizeof(struct create_fsync_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_reply, handle) == 8 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_reply, type) == 12 ); +C_ASSERT( FIELD_OFFSET(struct create_fsync_reply, shm_idx) == 16 ); +C_ASSERT( sizeof(struct create_fsync_reply) == 24 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_request, access) == 12 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_request, attributes) == 16 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_request, rootdir) == 20 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_request, type) == 24 ); +C_ASSERT( sizeof(struct open_fsync_request) == 32 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_reply, handle) == 8 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_reply, type) == 12 ); +C_ASSERT( FIELD_OFFSET(struct open_fsync_reply, shm_idx) == 16 ); +C_ASSERT( sizeof(struct open_fsync_reply) == 24 ); +C_ASSERT( FIELD_OFFSET(struct get_fsync_idx_request, handle) == 12 ); +C_ASSERT( sizeof(struct get_fsync_idx_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_fsync_idx_reply, type) == 8 ); +C_ASSERT( FIELD_OFFSET(struct get_fsync_idx_reply, shm_idx) == 12 ); +C_ASSERT( sizeof(struct get_fsync_idx_reply) == 16 ); +C_ASSERT( FIELD_OFFSET(struct fsync_msgwait_request, in_msgwait) == 12 ); +C_ASSERT( sizeof(struct fsync_msgwait_request) == 16 ); +C_ASSERT( sizeof(struct get_fsync_apc_idx_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_fsync_apc_idx_reply, shm_idx) == 8 ); +C_ASSERT( sizeof(struct get_fsync_apc_idx_reply) == 16 ); +C_ASSERT( FIELD_OFFSET(struct init_working_set_watch_request, fd) == 12 ); +C_ASSERT( sizeof(struct init_working_set_watch_request) == 16 ); +C_ASSERT( FIELD_OFFSET(struct get_ws_watches_request, process) == 12 ); +C_ASSERT( sizeof(struct get_ws_watches_request) == 16 ); +C_ASSERT( sizeof(struct get_ws_watches_reply) == 8 ); #endif /* WANT_REQUEST_HANDLERS */ diff --git a/server/thread.c b/server/thread.c index 4c51567c286..5163d540531 100644 --- a/server/thread.c +++ b/server/thread.c @@ -758,8 +758,21 @@ affinity_t get_thread_affinity( struct thread *thread ) unsigned int i; if (!sched_getaffinity( thread->unix_tid, sizeof(set), &set )) + { for (i = 0; i < 8 * sizeof(mask); i++) - if (CPU_ISSET( i, &set )) mask |= (affinity_t)1 << i; + if (CPU_ISSET( i, &set )) + { + if (thread->process->cpu_override.cpu_count) + { + if (i < ARRAY_SIZE(thread->process->wine_cpu_id_from_host)) + mask |= (affinity_t)1 << thread->process->wine_cpu_id_from_host[i]; + } + else + { + mask |= (affinity_t)1 << i; + } + } + } } #endif if (!mask) mask = ~(affinity_t)0; @@ -1672,8 +1685,7 @@ DECL_HANDLER(init_first_thread) else { set_thread_priority( current, current->process->priority, current->priority ); - if (!process->cpu_override.cpu_count) - set_thread_affinity( current, current->affinity ); + set_thread_affinity( current, current->affinity ); } debug_level = max( debug_level, req->debug_level ); @@ -1706,8 +1718,7 @@ DECL_HANDLER(init_thread) init_thread_context( current ); generate_debug_event( current, DbgCreateThreadStateChange, &req->entry ); set_thread_priority( current, current->process->priority, current->priority ); - if (!current->process->cpu_override.cpu_count) - set_thread_affinity( current, current->affinity ); + set_thread_affinity( current, current->affinity ); reply->suspend = (current->suspend || current->process->suspend || current->context != NULL); } diff --git a/server/trace.c b/server/trace.c index e971ba20b6f..39afbe199f4 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1487,6 +1487,8 @@ static void dump_init_process_done_request( const struct init_process_done_reque dump_uint64( " teb=", &req->teb ); dump_uint64( ", peb=", &req->peb ); dump_uint64( ", ldt_copy=", &req->ldt_copy ); + dump_uint64( ", kernel_stack=", &req->kernel_stack ); + fprintf( stderr, ", kernel_stack_size=%08x", req->kernel_stack_size ); } static void dump_init_process_done_reply( const struct init_process_done_reply *req ) @@ -1503,6 +1505,7 @@ static void dump_init_first_thread_request( const struct init_first_thread_reque fprintf( stderr, ", debug_level=%d", req->debug_level ); fprintf( stderr, ", reply_fd=%d", req->reply_fd ); fprintf( stderr, ", wait_fd=%d", req->wait_fd ); + fprintf( stderr, ", nice_limit=%c", req->nice_limit ); } static void dump_init_first_thread_reply( const struct init_first_thread_reply *req ) @@ -1522,6 +1525,8 @@ static void dump_init_thread_request( const struct init_thread_request *req ) fprintf( stderr, ", wait_fd=%d", req->wait_fd ); dump_uint64( ", teb=", &req->teb ); dump_uint64( ", entry=", &req->entry ); + dump_uint64( ", kernel_stack=", &req->kernel_stack ); + fprintf( stderr, ", kernel_stack_size=%08x", req->kernel_stack_size ); } static void dump_init_thread_reply( const struct init_thread_reply *req ) @@ -2098,14 +2103,14 @@ static void dump_recv_socket_request( const struct recv_socket_request *req ) { fprintf( stderr, " oob=%d", req->oob ); dump_async_data( ", async=", &req->async ); - fprintf( stderr, ", status=%08x", req->status ); - fprintf( stderr, ", total=%08x", req->total ); + fprintf( stderr, ", force_async=%d", req->force_async ); } static void dump_recv_socket_reply( const struct recv_socket_reply *req ) { fprintf( stderr, " wait=%04x", req->wait ); fprintf( stderr, ", options=%08x", req->options ); + fprintf( stderr, ", nonblocking=%d", req->nonblocking ); } static void dump_send_socket_request( const struct send_socket_request *req ) @@ -2340,7 +2345,8 @@ static void dump_read_process_memory_request( const struct read_process_memory_r static void dump_read_process_memory_reply( const struct read_process_memory_reply *req ) { - dump_varargs_bytes( " data=", cur_size ); + fprintf( stderr, " unix_pid=%d", req->unix_pid ); + dump_varargs_bytes( ", data=", cur_size ); } static void dump_write_process_memory_request( const struct write_process_memory_request *req ) @@ -2698,7 +2704,6 @@ static void dump_send_hardware_message_reply( const struct send_hardware_message fprintf( stderr, ", prev_y=%d", req->prev_y ); fprintf( stderr, ", new_x=%d", req->new_x ); fprintf( stderr, ", new_y=%d", req->new_y ); - dump_varargs_bytes( ", keystate=", cur_size ); } static void dump_get_message_request( const struct get_message_request *req ) @@ -2722,7 +2727,6 @@ static void dump_get_message_reply( const struct get_message_reply *req ) fprintf( stderr, ", x=%d", req->x ); fprintf( stderr, ", y=%d", req->y ); fprintf( stderr, ", time=%08x", req->time ); - fprintf( stderr, ", active_hooks=%08x", req->active_hooks ); fprintf( stderr, ", total=%u", req->total ); dump_varargs_message_data( ", data=", cur_size ); } @@ -2824,6 +2828,18 @@ static void dump_get_async_result_reply( const struct get_async_result_reply *re dump_varargs_bytes( " out_data=", cur_size ); } +static void dump_set_async_direct_result_request( const struct set_async_direct_result_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + dump_uint64( ", information=", &req->information ); + fprintf( stderr, ", status=%08x", req->status ); +} + +static void dump_set_async_direct_result_reply( const struct set_async_direct_result_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + static void dump_read_request( const struct read_request *req ) { dump_async_data( " async=", &req->async ); @@ -3428,12 +3444,9 @@ static void dump_get_thread_input_reply( const struct get_thread_input_reply *re fprintf( stderr, " focus=%08x", req->focus ); fprintf( stderr, ", capture=%08x", req->capture ); fprintf( stderr, ", active=%08x", req->active ); - fprintf( stderr, ", foreground=%08x", req->foreground ); fprintf( stderr, ", menu_owner=%08x", req->menu_owner ); fprintf( stderr, ", move_size=%08x", req->move_size ); fprintf( stderr, ", caret=%08x", req->caret ); - fprintf( stderr, ", cursor=%08x", req->cursor ); - fprintf( stderr, ", show_count=%d", req->show_count ); dump_rectangle( ", rect=", &req->rect ); } @@ -3541,6 +3554,15 @@ static void dump_set_caret_info_reply( const struct set_caret_info_reply *req ) fprintf( stderr, ", old_state=%d", req->old_state ); } +static void dump_get_active_hooks_request( const struct get_active_hooks_request *req ) +{ +} + +static void dump_get_active_hooks_reply( const struct get_active_hooks_reply *req ) +{ + fprintf( stderr, " active_hooks=%08x", req->active_hooks ); +} + static void dump_set_hook_request( const struct set_hook_request *req ) { fprintf( stderr, " id=%d", req->id ); @@ -4286,6 +4308,7 @@ static void dump_add_completion_request( const struct add_completion_request *re static void dump_remove_completion_request( const struct remove_completion_request *req ) { fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", waited=%d", req->waited ); } static void dump_remove_completion_reply( const struct remove_completion_reply *req ) @@ -4536,6 +4559,130 @@ static void dump_get_next_thread_reply( const struct get_next_thread_reply *req fprintf( stderr, " handle=%04x", req->handle ); } +static void dump_create_esync_request( const struct create_esync_request *req ) +{ + fprintf( stderr, " access=%08x", req->access ); + fprintf( stderr, ", initval=%d", req->initval ); + fprintf( stderr, ", type=%d", req->type ); + fprintf( stderr, ", max=%d", req->max ); + dump_varargs_object_attributes( ", objattr=", cur_size ); +} + +static void dump_create_esync_reply( const struct create_esync_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", type=%d", req->type ); + fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); +} + +static void dump_open_esync_request( const struct open_esync_request *req ) +{ + fprintf( stderr, " access=%08x", req->access ); + fprintf( stderr, ", attributes=%08x", req->attributes ); + fprintf( stderr, ", rootdir=%04x", req->rootdir ); + fprintf( stderr, ", type=%d", req->type ); + dump_varargs_unicode_str( ", name=", cur_size ); +} + +static void dump_open_esync_reply( const struct open_esync_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", type=%d", req->type ); + fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); +} + +static void dump_get_esync_fd_request( const struct get_esync_fd_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_get_esync_fd_reply( const struct get_esync_fd_reply *req ) +{ + fprintf( stderr, " type=%d", req->type ); + fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); +} + +static void dump_esync_msgwait_request( const struct esync_msgwait_request *req ) +{ + fprintf( stderr, " in_msgwait=%d", req->in_msgwait ); +} + +static void dump_get_esync_apc_fd_request( const struct get_esync_apc_fd_request *req ) +{ +} + +static void dump_create_fsync_request( const struct create_fsync_request *req ) +{ + fprintf( stderr, " access=%08x", req->access ); + fprintf( stderr, ", low=%d", req->low ); + fprintf( stderr, ", high=%d", req->high ); + fprintf( stderr, ", type=%d", req->type ); + dump_varargs_object_attributes( ", objattr=", cur_size ); +} + +static void dump_create_fsync_reply( const struct create_fsync_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", type=%d", req->type ); + fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); +} + +static void dump_open_fsync_request( const struct open_fsync_request *req ) +{ + fprintf( stderr, " access=%08x", req->access ); + fprintf( stderr, ", attributes=%08x", req->attributes ); + fprintf( stderr, ", rootdir=%04x", req->rootdir ); + fprintf( stderr, ", type=%d", req->type ); + dump_varargs_unicode_str( ", name=", cur_size ); +} + +static void dump_open_fsync_reply( const struct open_fsync_reply *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); + fprintf( stderr, ", type=%d", req->type ); + fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); +} + +static void dump_get_fsync_idx_request( const struct get_fsync_idx_request *req ) +{ + fprintf( stderr, " handle=%04x", req->handle ); +} + +static void dump_get_fsync_idx_reply( const struct get_fsync_idx_reply *req ) +{ + fprintf( stderr, " type=%d", req->type ); + fprintf( stderr, ", shm_idx=%08x", req->shm_idx ); +} + +static void dump_fsync_msgwait_request( const struct fsync_msgwait_request *req ) +{ + fprintf( stderr, " in_msgwait=%d", req->in_msgwait ); +} + +static void dump_get_fsync_apc_idx_request( const struct get_fsync_apc_idx_request *req ) +{ +} + +static void dump_get_fsync_apc_idx_reply( const struct get_fsync_apc_idx_reply *req ) +{ + fprintf( stderr, " shm_idx=%08x", req->shm_idx ); +} + +static void dump_init_working_set_watch_request( const struct init_working_set_watch_request *req ) +{ + fprintf( stderr, " fd=%d", req->fd ); +} + +static void dump_get_ws_watches_request( const struct get_ws_watches_request *req ) +{ + fprintf( stderr, " process=%04x", req->process ); +} + +static void dump_get_ws_watches_reply( const struct get_ws_watches_reply *req ) +{ + dump_varargs_ws_watch_data( " info=", cur_size ); +} + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_new_process_request, (dump_func)dump_get_new_process_info_request, @@ -4660,6 +4807,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_register_async_request, (dump_func)dump_cancel_async_request, (dump_func)dump_get_async_result_request, + (dump_func)dump_set_async_direct_result_request, (dump_func)dump_read_request, (dump_func)dump_write_request, (dump_func)dump_ioctl_request, @@ -4720,6 +4868,7 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_set_capture_window_request, (dump_func)dump_set_caret_window_request, (dump_func)dump_set_caret_info_request, + (dump_func)dump_get_active_hooks_request, (dump_func)dump_set_hook_request, (dump_func)dump_remove_hook_request, (dump_func)dump_start_hook_chain_request, @@ -4811,6 +4960,18 @@ 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_create_esync_request, + (dump_func)dump_open_esync_request, + (dump_func)dump_get_esync_fd_request, + (dump_func)dump_esync_msgwait_request, + (dump_func)dump_get_esync_apc_fd_request, + (dump_func)dump_create_fsync_request, + (dump_func)dump_open_fsync_request, + (dump_func)dump_get_fsync_idx_request, + (dump_func)dump_fsync_msgwait_request, + (dump_func)dump_get_fsync_apc_idx_request, + (dump_func)dump_init_working_set_watch_request, + (dump_func)dump_get_ws_watches_request, }; static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -4937,6 +5098,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { NULL, NULL, (dump_func)dump_get_async_result_reply, + (dump_func)dump_set_async_direct_result_reply, (dump_func)dump_read_reply, (dump_func)dump_write_reply, (dump_func)dump_ioctl_reply, @@ -4997,6 +5159,7 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_set_capture_window_reply, (dump_func)dump_set_caret_window_reply, (dump_func)dump_set_caret_info_reply, + (dump_func)dump_get_active_hooks_reply, (dump_func)dump_set_hook_reply, (dump_func)dump_remove_hook_reply, (dump_func)dump_start_hook_chain_reply, @@ -5088,6 +5251,18 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { NULL, NULL, (dump_func)dump_get_next_thread_reply, + (dump_func)dump_create_esync_reply, + (dump_func)dump_open_esync_reply, + (dump_func)dump_get_esync_fd_reply, + NULL, + NULL, + (dump_func)dump_create_fsync_reply, + (dump_func)dump_open_fsync_reply, + (dump_func)dump_get_fsync_idx_reply, + NULL, + (dump_func)dump_get_fsync_apc_idx_reply, + NULL, + (dump_func)dump_get_ws_watches_reply, }; static const char * const req_names[REQ_NB_REQUESTS] = { @@ -5214,6 +5389,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "register_async", "cancel_async", "get_async_result", + "set_async_direct_result", "read", "write", "ioctl", @@ -5274,6 +5450,7 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "set_capture_window", "set_caret_window", "set_caret_info", + "get_active_hooks", "set_hook", "remove_hook", "start_hook_chain", @@ -5365,6 +5542,18 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "suspend_process", "resume_process", "get_next_thread", + "create_esync", + "open_esync", + "get_esync_fd", + "esync_msgwait", + "get_esync_apc_fd", + "create_fsync", + "open_fsync", + "get_fsync_idx", + "fsync_msgwait", + "get_fsync_apc_idx", + "init_working_set_watch", + "get_ws_watches", }; static const struct @@ -5480,6 +5669,7 @@ static const struct { "PIPE_EMPTY", STATUS_PIPE_EMPTY }, { "PIPE_LISTENING", STATUS_PIPE_LISTENING }, { "PIPE_NOT_AVAILABLE", STATUS_PIPE_NOT_AVAILABLE }, + { "PORT_ALREADY_SET", STATUS_PORT_ALREADY_SET }, { "PORT_NOT_SET", STATUS_PORT_NOT_SET }, { "PREDEFINED_HANDLE", STATUS_PREDEFINED_HANDLE }, { "PRIVILEGE_NOT_HELD", STATUS_PRIVILEGE_NOT_HELD }, diff --git a/tools/Makefile.in b/tools/Makefile.in index adf53bb2c51..ff2da5e8584 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -5,4 +5,5 @@ C_SRCS = \ make_xftmpl.c IN_SRCS = \ + gdbinit.py.in \ wineapploader.in diff --git a/tools/gdbinit.py b/tools/gdbinit.py new file mode 100644 index 00000000000..ba3b7d003ac --- /dev/null +++ b/tools/gdbinit.py @@ -0,0 +1,113 @@ +#!/bin/env python3 + +# Copyright 2021 RĂ©mi Bernon for CodeWeavers +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + +from __future__ import print_function + +import gdb +import re +import subprocess +import sys + +class LoadSymbolFiles(gdb.Command): + 'Command to load symbol files directly from /proc//maps.' + + def __init__(self): + sup = super(LoadSymbolFiles, self) + sup.__init__('load-symbol-files', gdb.COMMAND_FILES, gdb.COMPLETE_NONE, + False) + + self.libs = {} + gdb.execute('alias -a lsf = load-symbol-files', True) + + def invoke(self, arg, from_tty): + pid = gdb.selected_inferior().pid + if not pid in self.libs: self.libs[pid] = {} + + def command(cmd, confirm=from_tty, to_string=not from_tty): + gdb.execute(cmd, from_tty=confirm, to_string=to_string) + + def execute(cmd): + return subprocess.check_output(cmd, stderr=subprocess.STDOUT) \ + .decode('utf-8') + + # load mappings addresses + libs = {} + with open('/proc/{}/maps'.format(pid), 'r') as maps: + for line in maps: + addr, _, _, _, node, path = re.split(r'\s+', line, 5) + path = path.strip() + if node == '0': continue + if path in libs: continue + libs[path] = int(addr.split('-')[0], 16) + + # unload symbol file if address changed + for k in set(libs) & set(self.libs[pid]): + if libs[k] != self.libs[pid][k]: + command('remove-symbol-file "{}"'.format(k), confirm=False) + del self.libs[k] + + # load symbol file for new mappings + for k in set(libs) - set(self.libs[pid]): + if arg is not None and re.search(arg, k) is None: continue + addr = self.libs[pid][k] = libs[k] + has_debug = False + offs = None + + try: + out = execute(['file', k]) + except: + continue + + # try loading mapping as ELF + try: + out = execute(['readelf', '-l', k]) + for line in out.split('\n'): + if not 'LOAD' in line: continue + base = int(line.split()[2], 16) + break + except: + # assume mapping is PE + base = -1 + + try: + name = None + cmd = 'add-symbol-file "{}"'.format(k) + out = execute(['objdump', '-h', k]) + for line in out.split('\n'): + if '2**' in line: + _, name, _, vma, _, off, _ = line.split(maxsplit=6) + if base < 0: offs = int(off, 16) + else: offs = int(vma, 16) - base + if 'ALLOC' in line: + cmd += ' -s {} 0x{:x}'.format(name, addr + offs) + elif name in ['.gnu_debuglink', '.debug_info']: + has_debug = True + elif 'DEBUGGING' in line: + has_debug = True + except: + continue + + if not has_debug: + print('no debugging info found in {}'.format(k)) + continue + + print('loading symbols for {}'.format(k)) + command(cmd, confirm=False, to_string=True) + + +LoadSymbolFiles() diff --git a/tools/gdbinit.py.in b/tools/gdbinit.py.in new file mode 120000 index 00000000000..9fb7fdf9b92 --- /dev/null +++ b/tools/gdbinit.py.in @@ -0,0 +1 @@ +gdbinit.py \ No newline at end of file