diff --git a/ANNOUNCE.md b/ANNOUNCE.md
index b5b501cb4c01..4480480be783 100644
--- a/ANNOUNCE.md
+++ b/ANNOUNCE.md
@@ -1,12 +1,12 @@
-The Wine development release 11.12 is now available.
+The Wine development release 11.13 is now available.
 
 What's new in this release:
-  - Bundled libswresample and libswscale from FFmpeg.
-  - Mono engine updated to version 11.2.0.
-  - XSLPattern parser reimplemented in MSXML.
+  - Better support for input pointers.
+  - Improved keyboard scancode mapping on X11.
+  - FFmpeg optimizations on ARM64EC.
   - Various bug fixes.
 
-The source is available at <https://dl.winehq.org/wine/source/11.x/wine-11.12.tar.xz>
+The source is available at <https://dl.winehq.org/wine/source/11.x/wine-11.13.tar.xz>
 
 Binary packages for various distributions will be available
 from the respective [download sites][1].
@@ -18,330 +18,303 @@ See the file [AUTHORS][3] for the complete list.
 
 [1]: https://gitlab.winehq.org/wine/wine/-/wikis/Download
 [2]: https://gitlab.winehq.org/wine/wine/-/wikis/Documentation
-[3]: https://gitlab.winehq.org/wine/wine/-/raw/wine-11.12/AUTHORS
+[3]: https://gitlab.winehq.org/wine/wine/-/raw/wine-11.13/AUTHORS
 
 ----------------------------------------------------------------
 
-### Bugs fixed in 11.12 (total 27):
-
- - #3037   LTspice, Progman: broken internal windows resizing
- - #18010  Slingplayer video tuning wizard fails
- - #27240  Multiple Corel product installers report msxml:xslpattern_error syntax error / msxml:domselection_create error code 1206 (Corel Paint Shop Photo Pro, CorelDRAW Graphics Suite X3/X4)
- - #33450  .NET 3.5 Framework installation fails (.NET WorkFlow Service Registration Tool "WFServicesReg.exe" crash with libxml2 < 2.9.0)
- - #34758  Microsoft Money 97 5.0 crashes with an unhandled divide by zero
- - #36912  Taskkill does not support /T option
- - #44232  Menus in Slingplayer 1.5 do not draw until mouseover
- - #48174  wine-mono creates incorrect strings
- - #51678  Avogadro crashes when adding atoms
- - #56100  Greenshot: Error when adding a textbox
- - #56664  winecfg not exposing an option for 7.1 surround sound audio output (winepulse.drv)
- - #58632  Against the Storm: Encyclopedia doesn't display animated sequences
- - #59317  Need for Speed Most Wanted (2005): Up is triggered continuously without pressing a button on gamepad
- - #59318  Graphic problem
- - #59406  Super Hexagon: if gamepad is connected, game will ignore every input after selecting anything from title screen
- - #59590  MS-Money 2000 - cannot process TKIND_UNION when loading INV7.OCX
- - #59711  unlock_fd: UnlockFile returns ERROR_LOCK_VIOLATION (33) instead of ERROR_NOT_LOCKED (158) when no matching lock is held
- - #59820  ComicRackCE freezes when attempting to view "info" for a comic file
- - #59842  Cross builds don't install the "wine" executable anymore
- - #59855  .configure script fails on system without "ln -s"
- - #59856  "musl: Use __builtin_rint if available" breaks rounding control in rint function family on x86_64
- - #59870  VRChat PhotonEncryptorPlugin.dll crash
- - #59871  Sonic Boom (1995 Klik & Play fangame) fails to create HBITMAP over memory, cannot start
- - #59880  For games installed under a prefix that has cnc_ddraw installed using Winetricks, error message appears
- - #59886  Build failed for SymCrypt with clang-msvc on x86/i386 and x86_64
- - #59894  MS Office 2007 does no longer accept product key from config file
- - #59910  One case of i386 unwinding broken
-
-### Changes since 11.11:
+### Bugs fixed in 11.13 (total 22):
+
+ - #13088  Adobe Acrobat Pro 7 / Acrobat Reader 7 -- Comments don't work
+ - #30824  msvbvm60.dll crashes Gehalt.exe
+ - #36373  Requiem: Avenging Angel - Black screen in-game
+ - #40231  Grand Theft Auto: Vice City crashes at start
+ - #42282  Tales of Zestiria v1.2 (latest Steam provided runtime) crashes at launch
+ - #45503  msiexec crashes during Office 2000 setup
+ - #49435  cyrrillic text are broken in winbox64.exe
+ - #50030  "Hayoola" freezes in wine 5.19 while it works great in 5.13
+ - #55030  Nelogica Profitchart freezes and Crash since wine-7.12 [bisect included]
+ - #55407  Virtual desktop option breaks wine creating windows
+ - #58105  explorer creates extra virtual desktop when using the /desktop=shell argument
+ - #58610  Sim Racing Studio fails to start: “Error Loading Prython DLL ”C:\Progam Files\RimsRacingStudio 2.0\python312.dll"
+ - #59727  DrawText rendering bug when DT_WORD_ELLIPSIS / DT_PATH_ELLIPSIS is used
+ - #59850  Exit unwind is broken
+ - #59884  Age of Mythology errors on startup with "Initialization failed"
+ - #59914  One i386 uwninding testcase fails with wine built with GCC
+ - #59916  d2d1 path geometry leaks figure->vertex_types (steady heap growth under sustained Direct2D path rendering)
+ - #59920  msvcrt setjmp/longjmp in x86_64 ELF builds broken by ntdll change
+ - #59932  Slingplayer 2's help has an extra window
+ - #59960  Sony Acid Pro 7.0 crashes on startup during initialization
+ - #59969  winecfg buttons can't be clicked on dpi != 96
+ - #59975  SuccubusHeaven camera breaks
+
+### Changes since 11.12:
 ```
-Alexandre Julliard (20):
-      makefiles: Don't reuse an install command if it has an explicit destination name.
-      makefiles: Always enable the wine tool.
-      makefiles: Also skip C files in arch-specific directories.
-      libs: Import ffmpeg from upstream release 8.1.1.
-      user32: Don't send a message in GetComboBoxInfo().
-      nsiproxy.sys: Link to pthread libs for pthread_once.
-      ffmpeg: Import upstream assembly optimizations for ARM64.
-      ffmpeg: Import upstream assembly optimizations for ARM.
-      makefiles: Add support for nasm.
-      ffmpeg: Import upstream assembly optimizations for x86.
-      symcrypt: Disable asm optimizations globally for non-PE builds.
-      symcrypt: Disable asm optimizations for older clang versions.
-      ffmpeg: Only disable clang warnings when building ffmpeg itself.
-      configure: Use the pkg-config specified libs by default in package checks.
-      configure: Use the pkg-config specified libs also for MinGW checks.
-      configure: Add more pkg-config checks.
-      kernelbase: Implement HasFiberData thread flag.
-      ntdll: Initialize fiber data to 0x1e00.
-      ntdll: Set default NtGlobalFlag for debugged processes.
-      server: Fix returned status for NtUnlockFile on unlocked range.
-
-Alistair Leslie-Hughes (4):
-      tiptsf: Add dll.
-      tiptsf: Add ITextInputPanel interface stub.
-      rtscom: Add stub dll.
-      rtscom: Add RealTimeStylus stubbed interface.
-
-André Zwing (6):
-      comctl32/tests: Fix typo for ARM64EC.
-      kernel32/tests: Fix typo for ARM64EC.
-      oleaut32/tests: Fix typo for ARM64EC.
-      user32/tests: Fix typo for ARM64EC.
-      uxtheme/tests: Fix typo for ARM64EC.
-      vcomp/tests: Fix typo for ARM64EC.
-
-Anna (navi) Figueiredo Gomes (4):
-      win32u: Stub NtUserGetPointerType.
-      win32u: Stub NtUserGetPointerDeviceRects.
-      win32u/tests: Add NtUserGetPointerDeviceRects test for INVALID_HANDLE_VALUE.
-      win32u: Implement NtUserGetPointerDeviceRects for INVALID_HANDLE_VALUE.
-
-Bartosz Kosiorek (5):
-      msvcp: Use sizeof(wchar_t) instead of sizeof(char) for basic_string_wchar_replace_cstr_len.
-      msvcp: Use sizeof(wchar_t) instead of sizeof(char) for basic_string_wchar_replace_ch.
-      msvcp90: Add _Ctraits<float>::_Isinf(float) implementation.
-      msvcp90: Add _Ctraits<double>::_Isinf(double) implementation.
-      msvcp90: Add _Ctraits<long double>::_Isinf(long double) implementation.
-
-Benoît Legat (1):
-      rsaenh: Fix reporting of "Cryptographic Service Provider".
-
-Bernhard Übelacker (9):
-      mfplat/tests: Avoid crash if mediasource retrieval failed.
-      mfplat/tests: Explicitly link against kernel32 before kernelbase.
-      mfsrcsnk/tests: Add broken in test_mp4_extra_metadata.
-      user32/tests: Fix test_class_name with old Windows versions.
-      dxgi/tests: Add two missing winetest_pop_context.
-      dxgi/tests: Skip tests if DXGI 1.4 is not available.
-      msxml3/xpath: Fix out-of-bounds access in query string (ASan).
-      oleview: Avoid heap-use-after-free when reloading (ASan).
-      regedit: Avoid heap-buffer-overflow in regedit with REG_MULTI_SZ (ASan).
-
-Connor McAdams (1):
-      dinput: Lowercase device paths passed into insert_cache_entry() when reusing cache entries.
-
-Daniel Lehman (4):
-      msxml3/tests: Fix double-free of node text.
-      dwrite: Add fallbacks for arrows.
-      include: Move ifdef __cplusplus in rpcasync.h.
-      shell32: Use DE_INVALIDFILES instead of hard-coded value.
-
-Dmitry Timoshkov (2):
-      oleaut32: Take into account that SAFEARRAY element in an SLTG typelib uses 32-bit units.
-      gdi.exe: Don't use biSizeImage when calculating DIB pitch.
-
-Esme Povirk (1):
-      mscoree: Update Wine Mono to 11.2.0.
-
-Etaash Mathamsetty (6):
-      winewayland: Implement fractional scaling protocol.
-      winewayland: Fix missing wayland_win_data_release.
-      win32u: Always flush the window surface in expose_window_surface.
-      winewayland: Assign a name to the winewayland queue.
-      winewayland: Change default debug channel to cursor for pointer.
-      winewayland: Rename toplevel_hwnd to owner_hwnd.
-
-Feli Rippmann (1):
-      comctl32: Clamp rebar height to cyMaxChild when cyIntegral == 0 too.
-
-Floris Renaud (1):
-      po: Update Dutch translation.
-
-Francis De Brabandere (6):
-      vbscript: Index named items by name.
-      vbscript: Store global variables and functions in a single tagged tree.
-      vbscript: Parse a bare '&' followed by octal digits as an octal literal.
-      vbscript: Cache host DISPIDs as entries in the unified global tree.
-      vbscript: Raise type mismatch when comparing an array.
-      vbscript: Look up named items before builtin functions.
-
-Giang Nguyen (1):
-      d2d1: Free the vertex types array in d2d_figure_cleanup.
-
-Hans Leidekker (4):
-      bcrypt: Fix parameter validation for GCM mode.
-      bcrypt: Remove unused fields.
-      symcrypt: Accept RSA keys with public exponent 1.
-      bcrypt: Reject RSA keys with public exponent 1.
-
-Henri Verbeet (2):
-      d3dcompiler: Retrieve signature information using vkd3d in d3dcompiler_shader_reflection_init().
-      d3dcompiler: Retrieve thread group size information using vkd3d in d3dcompiler_shader_reflection_init().
-
-Jacek Caban (12):
-      dpnet: Remove unused variables.
-      rpcrt4/tests: Remove unused variables.
-      riched20/tests: Remove unused variables.
-      msxml6/tests: Enable call checks in set_xht_site.
-      msxml3/tests: Remove unused code.
-      msxml3/tests: Add missing CLEAR_CALLED statement.
-      mf/tests: Remove unused variables.
-      wbemprox: Remove unused variable.
-      winebus: Remove unused variables.
-      joy.cpl: Remove unused variable.
-      winevulkan: Remove unused variable.
-      winex11: Remove unused variables.
-
-Marius Kamm (2):
-      windowscodecs: Fix decoding GIFs that fill the LZW code table.
-      windowscodecs/tests: Test decoding a GIF that fills the LZW code table.
-
-Martin Storsjö (2):
-      ntdll: Don't validate arm64 pointer signing when unwinding.
-      include: Use __msvcrt_ulong in setjmp.h.
-
-Matteo Bruni (1):
-      dsound: Get rid of non-float mixing remnants.
+Alex Henrie (1):
+      faudio: Handle odd offsets in decode_mono_adpcm_block.
+
+Alexandre Julliard (6):
+      ntdll: Fix handling of exit unwind when there are no TEB frames.
+      msvcrt: Save all registers when calling the exception filter on i386.
+      ffmpeg: Add support for ARM64EC assembly.
+      ffmpeg: Add ARM64EC assembly in libswresample.
+      ffmpeg: Add ARM64EC assembly in libswscale.
+      make_announce: Use the new Bugzilla REST API.
+
+Alexey Prokhin (1):
+      win32u: Enable fullscreen clipping even if not already clipping.
+
+Alistair Leslie-Hughes (14):
+      dplayx: Add missing break.
+      fltmgr.sys: Add FltAllocateContext stub.
+      fltmgr.sys: Add FltReleaseContext stub.
+      fltmgr.sys: Add FltGetStreamContext stub.
+      fltmgr.sys: Add FltSetStreamContext stub.
+      fltmgr.sys: Add FltQueryInformationFile stub.
+      fltmgr.sys: Add FltGetFileNameInformationUnsafe stub.
+      fltmgr.sys: Add FltReadFile stub.
+      fltmgr.sys: Add FltParseFileNameInformation stub.
+      fltmgr.sys: Add FltReleaseFileNameInformation stub.
+      fltmgr.sys: Add FltGetFileNameInformation stub.
+      fltmgr.sys: Add FltCreateCommunicationPort stub.
+      fltmgr.sys: Add FltCloseCommunicationPort stub.
+      fltmgr.sys: Add FltEnumerateVolumes stub.
+
+Anna (navi) Figueiredo Gomes (9):
+      win32u: Move process_pointer_message to input.c.
+      win32u: Keep per-thread list of known pointers.
+      win32u: Preallocate pointerId 1 for the mouse pointer.
+      win32u: Keep track of pointer types.
+      win32u: Implement NtUserGetPointerType.
+      win32u: Hardcode pointer id 1 to PT_MOUSE in GetPointerType.
+      win32u: Update pointerid 1 from mouse_in_pointer events.
+      server: Mask pointer inputs for SetCursorPos requests.
+      win32u: Implement NtUserGetPointerInfoList.
+
+Anton Baskanov (7):
+      dmsynth: Increase the write latency by 10 ms.
+      dsound: Name the parameters of bitsgetfunc and bitsputfunc.
+      dsound: Remove getieee32 from the getbpp array.
+      dsound: Get rid of endianness conversion.
+      dsound: Downmix to mono after resampling and remove get_mono.
+      dsound: Get rid of get_aux and read the values directly.
+      dsound: Get all channel samples in one go.
+
+Benoît Legat (2):
+      crypt32: Avoid conflict between unnamed containers.
+      crypt32: Open MS_ENH_RSA_AES_PROV in PFXImportCertStore.
+
+Bernhard Übelacker (1):
+      secur32/tests: Update count for new test.winehq.org certificate.
+
+Brendan Shanks (4):
+      ntdll: Advertise ARM32 processor features on ARM64.
+      ntdll: Detect ARM32 processor features using getauxval(AT_HWCAP).
+      ntdll: Detect ARM64 processor features on macOS, and on Linux using getauxval(AT_HWCAP).
+      ntdll: Define HWCAP_CPUID on ARM64.
+
+Conor McCarthy (11):
+      windows.storage/tests: Test InMemoryRandomAccessStream.
+      windows.storage: Add InMemoryRandomAccessStream stubs.
+      windows.storage: Implement InMemoryRandomAccessStream IClosable::Close().
+      windows.storage: Implement InMemoryRandomAccessStream IOutputStream::WriteAsync().
+      windows.storage: Implement InMemoryRandomAccessStream IRandomAccessStream::get_Size().
+      windows.storage: Implement InMemoryRandomAccessStream IRandomAccessStream::put_Size().
+      windows.storage: Implement InMemoryRandomAccessStream IRandomAccessStream::get_Position().
+      windows.storage: Implement InMemoryRandomAccessStream IRandomAccessStream::Seek().
+      windows.storage: Implement InMemoryRandomAccessStream IInputStream::ReadAsync().
+      windows.storage: Add InMemoryRandomAccessStream to class registration.
+      windows.storage: Do not limit the size to SIZE_MAX in memory_stream_random_access_put_Size().
+
+Daniel Lehman (1):
+      dwrite: Add some more fallback ranges.
+
+Elizabeth Figura (18):
+      wined3d: Store plane_formats as wined3d_format pointers.
+      wined3d: Allow any typed format for planar texture views.
+      wined3d: Do not alter the swapchain latency.
+      wined3d: Use a semaphore for frame latency.
+      dxgi: Implement DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT for d3d11.
+      dxgi: Implement d3d11_swapchain_SetMaximumFrameLatency().
+      dxgi: Implement d3d11_swapchain_GetMaximumFrameLatency().
+      dxgi: Implement d3d11_swapchain_GetFrameLatencyWaitableObject().
+      acledit: No longer prefer native.
+      avrt: No longer prefer native.
+      connect: No longer prefer native.
+      inkobj: No longer prefer native.
+      magnification: No longer prefer native.
+      mscat32: No longer prefer native.
+      msnet32: No longer prefer native.
+      msports: No longer prefer native.
+      mssip32: No longer prefer native.
+      netprofm: No longer prefer native.
+
+Erhan Bilgili (1):
+      cfgmgr32: Fix CM_Get_Device_ID buffer size handling.
+
+Eric Pouech (1):
+      dbghelp/dwarf: Fix out-of-bound write when leaf name is too large.
+
+Etaash Mathamsetty (4):
+      winex11: Don't initialize win_data_mutex twice.
+      winewayland: Don't initialize win_data_mutex twice.
+      winewayland: Use a flag instead of checking zwp_relative_pointer_v1.
+      winewayland: Don't offset client rect in client surface reconfigure.
+
+Francis De Brabandere (1):
+      vbscript: Count carriage returns as line endings when reporting error lines.
+
+Gabriel Ivăncescu (7):
+      mshtml: Don't leak gecko attr when creating attribute.
+      mshtml: Don't leak ref from attribute collection when detaching attribute.
+      mshtml: Don't leak gecko attribute when obtaining DISPID from collection.
+      mshtml: Properly unlink the element refs of frame elements.
+      mshtml: Properly unlink the element refs of iframe elements.
+      mshtml/tests: Fix attribute leak when testing tabIndex.
+      mshtml/tests: Properly close the view of the legacy document when testing attributes across modes.
+
+Haoyang Chen (1):
+      ole32: Add support for writing VT_BOOL properties.
+
+Jacek Caban (4):
+      dsound/tests: Remove unused variable.
+      qcap/tests: Remove unused variable.
+      ws2_32/tests: Remove unused variable.
+      http.sys: Check thread_stop in request_thread_proc loop.
+
+Jacob Czekalla (1):
+      hhctrl.ocx: Create the index popup window hidden.
+
+Jiajin Cui (6):
+      wininet/tests: Cover long CreateUrlCacheEntryW extensions.
+      urlmon/tests: Cover URLDownloadToCacheFile buffer bounds.
+      wininet: Ignore overly long cache file extensions.
+      urlmon: Fix URLDownloadToCacheFile buffer handling.
+      urlmon: Remove temporary cache files on failure.
+      winewayland: Fix y coordinate of IME cursor rectangle.
+
+Louis Lenders (3):
+      kernel32: Add stub for PssQuerySnapshot.
+      kernel32: Add stub for PssFreeSnapshot.
+      kernel32: Add stub for PssCaptureSnapshot.
+
+Maotong Zhang (1):
+      user32: Fix lastSlash handling in TEXT_PathEllipsify.
+
+Martin Storsjö (1):
+      configure: Reduce the fixed load address for the preloader on aarch64.
+
+Matteo Bruni (4):
+      winex11: Fix Right Shift scancode.
+      user32/tests: Add a SendInput VK_PAUSE test.
+      user32/tests: Add more key names tests.
+      user32/tests: Add more key map tests.
 
 Nello De Gregoris (1):
-      mfsrcsnk: Use SetCurrentPosition() for absolute byte stream seeks.
-
-Nikolay Sivov (15):
-      dsrole: Add a new dll.
-      netapi32: Forward DsRole* functions to dsrole.dll.
-      msxml3/tests: Add more selection tests.
-      msxml3/xpath: Remove duplicate line typo.
-      msxml3/tests: Add some tests for node value comparison in queries.
-      msxml3/xpath: Fix comparison direction for node-set vs value case.
-      msxml3/tests: Add some more comparison tests for XPath.
-      msxml3: Add a helper to check for XPath query keywords.
-      msxml3: Explicitly switch to XPath for XmlView documents.
-      msxml3: Handle XSLPattern syntax directly instead of converting to XPath.
-      msxml3: Fix a typo in a helper name.
-      msxml3/tests: Add a query test with attribute test in the predicate.
-      msxml3: Add support for nodeName() function.
-      msxml3: Rename helpers to reflect their purpose better.
-      msxml3: Remove now unused file.
-
-Pan Hui (1):
-      winewayland: Use process_name in wl_display_create_queue_with_name for debugging.
-
-Paul Gofman (21):
-      nsiproxy.sys: Factor out icmp_socket structure.
-      nsiproxy.sys: Rename icmp_listen into icmp_get_reply.
-      nsiproxy.sys: Contain all the reply data in struct icmp_reply_ctx.
-      nsiproxy.sys: Manage ICMP listen threads on the Unix side.
-      nsiproxy.sys: Poll all icmp sockets in a single thread.
-      nsiproxy.sys: Return ICMP ids from parse_icmp_hdr() instead of matching those.
-      nsiproxy.sys: Reuse icmp socket with matching parameters.
-      nsiproxy.sys: Support ICMP error replies with ping sockets.
-      nsiproxy.sys: Use compatible ICMP request id and seq numbers.
-      winevulkan: Do not show debug assertion dialog on Unix side exceptions.
-      win32u: Support D3D12_FENCE_SUBMIT_INFO in win32u_vkQueueSubmit().
-      win32u: Force timeline semaphore type when importing d3d12 fence.
-      ntdll/tests: Add tests for PROCESS_PARAMS_IMAGE_KEY_MISSING flag.
-      ntdll: Manage PROCESS_PARAMS_IMAGE_KEY_MISSING flag.
-      imm32/tests: Flush events in test_ImmSetCompositionWindow().
-      imm32/tests: Add tests for resetting in-composition state.
-      imm32: Strip off trailing zeros in ImeSetCompositionString().
-      imm32: Clear in-composition state when empty composition string is set.
-      imm32: Reset composition string when deactivating context.
-      wbemprox: Use device instance id for pnpdevice_id in get_display_adapters().
-      ntdll: Don't reserve exc_stack_layout twice in call_user_exception_dispatcher() on i386.
-
-Piotr Caban (10):
-      mshtml: Added IHTMLTextAreaElement::disabled property implementation.
-      msv1_0: Initialize seal keys in ntlm_SpAcceptLsaModeContext.
-      ucrtbase/tests: Remove no longer needed workaround from test_math_errors.
-      ucrtbase: Don't use builtin_rint since it doesn't respect rounding control.
-      secur32/tests: Cleanup ntlm negotiate flags tests.
-      secur32/tests: Test ctxt_attrs value after initial InitializeSecurityContextA call.
-      msv1_0: Fix buffer type returned by ntlm_SpAcceptLsaModeContext.
-      msv1_0: Make output argument optional on second call to ntlm_SpAcceptLsaModeContext.
-      msv1_0: Set ctxt_attr in ntlm_SpInitLsaModeContext.
-      msv1_0: Set ctxt_attr in ntlm_SpAcceptLsaModeContext.
-
-Rose Hellsing (4):
-      gdiplus: Recognize raw WMF streams in the WMF image codec.
-      gdiplus: Rasterize metafiles for encoding and report raw WMF as EMF.
-      gdiplus: Store the source HMETAFILE for proper lifetime management.
-      gdiplus/tests: Add tests for raw WMF load, playback and encoding.
-
-Rémi Bernon (50):
-      xinput1_3: Introduce a new open_device_at_index helper.
-      mf/topology_loader: Restore original type when optional connection fails.
-      opengl32: Implement client / host mapping for shader names.
-      opengl32: Use a per-table lock for context / pbuffer / sync handles.
-      opengl32: Move the sync handle table to the display lists.
-      opengl32: Trace client side object names leaks.
-      opengl32: Remove unused unix thunks.
-      ffmpeg: Redirect av_log calls to Wine debug traces.
-      winegstreamer: Remove unnecessary resampler inline qualifiers.
-      winegstreamer/resampler: Implement IMediaObject iface using libswresample.
-      winegstreamer/resampler: Translate IMFTransform calls to IMediaObject.
-      resampledmo: Move resampler implementation from winegstreamer.
-      win32u: Add a new driver entry to create client surfaces.
-      win32u: Create vulkan client surfaces from win32u.
-      win32u: Create OpenGL client surfaces from win32u.
-      user32/tests: Use 1024x768 as test resolution for DPI scaling on Windows.
-      user32/tests: Add more GetPointerDeviceRects tests with DPI scaling.
-      opengl32: Unset current context on thread detach.
-      win32u: Create a default thread context when binding NULL.
-      opengl32: Ensure a context is current before display lists destruction.
-      win32u: Share all unix-side GL contexts with a global context.
-      opengl32: Keep a global WOW64 buffer storage map.
-      opengl32: Implement wglShareLists on the PE side.
-      win32u: Get rid of opengl_drawable set_context callback.
-      opengl32: Allocate WOW64 buffers struct with calloc.
-      opengl32: Set dummy context with wglMakeContextCurrentARB.
-      gitlab: Add nasm to the CI docker images.
-      xinput1_3: Move keystroke states and locks to check_for_keystroke.
-      xinput1_3: Read and write current state without the controller lock.
-      xinput1_3: Use a global critical section to guard device and enabled flag.
-      xinput1_3: Extend the CS to guard all device open / read / destroy.
-      winegstreamer/color_convert: Remove unnecessary inline qualifiers.
-      winegstreamer/color_convert: Implement IMediaObject iface using libswscale.
-      winegstreamer/color_convert: Translate IMFTransform calls to IMediaObject.
-      colorcnv: Move color converter implementation from winegstreamer.
-      winegstreamer/video_processor: Reimplement using libswscale.
-      msvproc: Move video processor implementation from winegstreamer.
-      opengl32: Only query GL_CONTEXT_PROFILE_MASK with GL >= 3.2.
-      opengl32: Only reserve name range if range length is > 0.
-      opengl32: Remove now unnecessary context creation attribs.
-      winewayland: Make sure the primary monitor rect is at 0x0.
-      winewayland: Use RECT / POINT to map coordinates from surface.
-      winewayland: Use RECT / POINT to map coordinates to surface.
-      winewayland: Use a RECT in struct wayland_surface_config.
-      win32u: Use the null context instead of internal context for pbuffers.
-      win32u: Notify driver of capture window reset on destruction.
-      win32u: Pass current and previous toplevel windows to SetCapture.
-      winex11: Also ignore warped MotionNotify if cursor isn't clipped.
-      winex11: Simplify send_mouse_input when clipping cursor.
-      winex11: Get rid of now unnecessary x11drv_thread_data()->grab_hwnd.
-
-Shaun Ren (10):
-      xinput1_3: Remove an unreachable return from HID_set_state().
-      xinput1_3: Keep controller read events across device changes.
-      xinput1_3: Return correct slot indices for existing devices in find_opened_device().
-      xinput1_3: Don't wait on overlapped event if CancelIoEx failed.
-      xinput1_3: Clear enabled when controller is destroyed on read error.
-      xinput1_3: Return FALSE from controller_init if device enable failed.
-      xinput1_3: Disable controller when removing an opened device.
-      xinput1_3: Rescan controllers before reporting disconnection to callers.
-      xinput1_3: Initialize state with the previous state in read_controller_state.
-      xinput1_3: Initialize controller state only on success in controller_init.
-
-Soham Nandy (1):
-      ntdll: Ignore hardware breakpoint traps inside the signal stack.
+      mfsrcsnk: Ignore empty byte stream origin names.
+
+Nikolay Sivov (4):
+      msxml3/xpath: Add a strstr() variant that handles nulls.
+      msxml3/xpath: Add a string comparison helper that handles nulls.
+      msxml3/tests: Add a test for translate() function.
+      msxml3/xpath: Fix translate() function to actually return its result.
+
+Paul Gofman (4):
+      ws2_32: Add WSCEnumProtocols32().
+      kernel32/tests: Test filename case preservation when opening existing file.
+      kernelbase: Add a stub for FindNextFileNameW().
+      mmdevapi: Implement SAORS_Reset().
+
+Piotr Caban (3):
+      secur32: Return impersonation token from lsa_QuerySecurityContextToken.
+      msv1_0: Decrypt all data buffers in ntlm_SpUnsealMessage.
+      gdiplus: Return InvalidParameter error when GdipLoadImageFromStream is used with empty stream.
+
+Piotr Pawłowski (2):
+      comctl32/tests: Add tests for TVN_ITEMCHANGING and TVN_ITEMCHANGED.
+      comctl32/treeview: Implemented TVN_ITEMCHANGING and TVN_ITEMCHANGED.
+
+Ryan Houdek (1):
+      wow64: Add BTCpuNotifyProcessExecuteFlagsChange interface.
+
+Rémi Bernon (53):
+      winewayland: Make window lock mutex recursive.
+      winewayland: Use update_client_surfaces to detach client surfaces.
+      winewayland: Remove unnecessary client surface attach / detach.
+      winewayland: Move ensure_window_surface_contents into wayland_surface.c.
+      winex11: Use the desired state for mapping delays.
+      server: Map update_window_zorder rect from raw to virtual coordinates.
+      server: Update window zorder directly in queue_mouse_message.
+      wineandroid: Remove now unnecessary update_window_zorder call.
+      winemac: Remove now unnecessary update_window_zorder call.
+      winex11: Remove now unnecessary update_window_zorder call.
+      win32u: Keep track of client surface toplevel window.
+      win32u: Move client_surface_update on present from winex11.
+      win32u: Move client surface rect computation out of the drivers.
+      win32u: Assume that NtUserExposeWindowSurface rect is in raw monitor DPI.
+      win32u: Reorder KEYEVENTF_SCANCODE handling control flow.
+      win32u: Remap some scancodes to better match native behavior.
+      winex11: Use scancode high bit to set KEYEVENTF_EXTENDEDKEY flag.
+      winex11: Support fixed X11 keycode to scancode conversion.
+      opengl32/tests: Test versioned / core / compat context creation and sharing.
+      winemac: Remove unnused macdrv_context renderer_id member.
+      opengl32: Remove duplicated client context member.
+      winex11: Remove unnecessary window send_mouse_input argument.
+      winex11: Pass individual INPUT fields to send_mouse_input.
+      winex11: Pass / return a POINT to / from map_event_coords.
+      winex11: Use send_mouse_input in move_resize_window.
+      winex11: Use send_mouse_input in the RawMotion handler.
+      win32u: Use internal helpers to get present / client rects and dpi.
+      win32u: Introduce a ratio struct for unix-side DPI values.
+      server: Use the ratio struct for DPI values.
+      win32u: Represent raw monitor dpi as rational value.
+      server: Move window monitor DPI to the shared memory.
+      win32u: Ignore empty mouse input in NtUserSendHardwareInput.
+      server: Skip dispatching rawinput message when it is empty.
+      win32u: Accumulate mouse motion in NtUserSendHardwareInput.
+      winex11: Remove now unnecessary mouse motion event merging.
+      winemac: Try initializing GL3_Core and GL4_Core profile contexts.
+      winemac: Create all core contexts with the best supported profile.
+      winemac: Use WGL_CONTEXT_CORE_PROFILE_BIT_ARB to detect core profiles.
+      win32u: Request core profile for internal OpenGL contexts.
+      winemac: Ignore context sharing when profile doesn't match.
+      opengl32: Flag GL_EXT_external_buffer extension as unsupported.
+      opengl32: Move more wow64 wrappers out of the generated code.
+      opengl32: Use a static variable for the buffer storage map.
+      opengl32: Remove unused flags member from buffer struct.
+      opengl32: Reduce WOW64 locking to the buffer storage map accesses.
+      server: Always update window monitor DPI from its parent.
+      winewayland: Remove now unnecessary implicit fullscreen clipping.
+      server: Support explicit raw mouse motion in NtUserSendHardwareInput.
+      winewayland: Send raw mouse motion through NtUserSendHardwareInput.
+      winex11: Enable XI2 raw events when window is focused.
+      winex11: Send raw mouse motion frames from XI2 RawEvents.
+      winex11: Remove unnecessary ConfigureNotify event merging.
+      winex11: Get rid of now unnecessary event merging logic.
+
+Stefan Riesenberger (2):
+      dnsapi: Add stub for DnsStartMulticastQuery().
+      dnsapi: Add stub for DnsStopMulticastQuery().
+
+Sven Baars (2):
+      configure: Set PCSCLITE_LIBS in the AC_CHECK_FUNC error case on macOS.
+      dxgi: Replace FIXMEs in swapchain latency methods by traces.
 
 Thomas Portal (1):
-      user32: Add a stub for IsImmersiveProcess().
-
-Tobias Markus (1):
-      ksecdd.sys: Forward BCrypt functions to bcrypt.dll.
-
-Vibhav Pant (1):
-      winebth.sys: Ensure bus relations are invalidated after the PDO has been started by the PnP manager.
-
-Wolfgang Hartl (1):
-      oleaut32: Implement SLTG union type processing.
-
-Zhengyong Chen (1):
-      windowscodecs: Decode GIF frames on demand.
-
-Zhiyi Zhang (1):
-      server: Fix a memory leak in d3dkmt_share_objects.
-
-Ziqing Hui (1):
-      dwrite: Fix Miscellaneous Symbols font fallback.
+      hidclass: Return the full report length from IOCTL_HID_WRITE_REPORT.
+
+Zhiyi Zhang (14):
+      dxgi/tests: Wait for destroyed windows to disappear before creating a fullscreen swapchain.
+      dxgi/tests: Run test_create_swapchain() on D3D12 as well.
+      dxgi/tests: Test creating a fullscreen swapchain with an invalid scanline order or scaling.
+      dxgi: Report the DXGI_SWAP_CHAIN_FULLSCREEN_DESC used for creating a D3D11 swapchain.
+      dxgi: Create a windowed swapchain instead when the fullscreen mode scanline order or scaling is invalid.
+      dxgi: Refuse to enter fullscreen when the fullscreen mode scanline order or scaling is invalid.
+      dxgi: Retrieve the fullscreen state from the swapchain state in d3d11_swapchain_GetFullscreenState().
+      user32/tests: Move the test window position further away from the top-left corner of the work area.
+      user32/tests: Fix test failures when the top-left corner of the work area is not (0,0).
+      user32/tests: Add more window placement tests.
+      win32u: Implement coordinates mapping in {Get,Set}WindowPlacement().
+      notepad: Use SetWindowPlacement() to restore the saved normal window position.
+      taskmgr: Use SetWindowPlacement() to restore the saved normal window position.
+      wordpad: Use SetWindowPlacement() to restore the saved normal window position.
 ```
diff --git a/AUTHORS b/AUTHORS
index 54f93cd01591..b0ca114da3ba 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -529,6 +529,7 @@ Emmanuel Maillard
 Enol Puente
 Enrico Horn
 Erez Volk
+Erhan Bilgili
 Eric Anholt
 Eric Bissonnette
 Eric Frias
diff --git a/VERSION b/VERSION
index 38fd2a0bbbfc..0e19529b6b29 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-Wine version 11.12
+Wine version 11.13
diff --git a/configure b/configure
index 5cd54ad7b4db..73a4d6a4b6b5 100755
--- a/configure
+++ b/configure
@@ -1,6 +1,6 @@
 #! /bin/sh
 # Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.73 for Wine 11.12.
+# Generated by GNU Autoconf 2.73 for Wine 11.13.
 #
 # Report bugs to <wine-devel@winehq.org>.
 #
@@ -587,8 +587,8 @@ MAKEFLAGS=
 # Identity of this package.
 PACKAGE_NAME='Wine'
 PACKAGE_TARNAME='wine'
-PACKAGE_VERSION='11.12'
-PACKAGE_STRING='Wine 11.12'
+PACKAGE_VERSION='11.13'
+PACKAGE_STRING='Wine 11.13'
 PACKAGE_BUGREPORT='wine-devel@winehq.org'
 PACKAGE_URL='https://www.winehq.org'
 
@@ -2509,7 +2509,7 @@ if test "$ac_init_help" = "long"; then
   # Omit some internal or obsolete options to make the list less imposing.
   # This message is too long to be a string in the A/UX 3.1 sh.
   cat <<_ACEOF
-'configure' configures Wine 11.12 to adapt to many kinds of systems.
+'configure' configures Wine 11.13 to adapt to many kinds of systems.
 
 Usage: $0 [OPTION]... [VAR=VALUE]...
 
@@ -2579,7 +2579,7 @@ fi
 
 if test -n "$ac_init_help"; then
   case $ac_init_help in
-     short | recursive ) echo "Configuration of Wine 11.12:";;
+     short | recursive ) echo "Configuration of Wine 11.13:";;
    esac
   cat <<\_ACEOF
 
@@ -2940,7 +2940,7 @@ fi
 test -n "$ac_init_help" && exit $ac_status
 if $ac_init_version; then
   cat <<\_ACEOF
-Wine configure 11.12
+Wine configure 11.13
 generated by GNU Autoconf 2.73
 
 Copyright (C) 2026 Free Software Foundation, Inc.
@@ -3346,7 +3346,7 @@ cat >config.log <<_ACEOF
 This file contains any messages produced by compilers while
 running configure, to aid debugging if configure makes a mistake.
 
-It was created by Wine $as_me 11.12, which was
+It was created by Wine $as_me 11.13, which was
 generated by GNU Autoconf 2.73.  Invocation command line was
 
   $ $0$ac_configure_args_raw
@@ -10788,6 +10788,12 @@ if test "x$ac_cv_header_arpa_nameser_h" = xyes
 then :
   printf '%s\n' "#define HAVE_ARPA_NAMESER_H 1" >>confdefs.h
 
+fi
+ac_fn_c_check_header_compile "$LINENO" "asm/hwcap.h" "ac_cv_header_asm_hwcap_h" "$ac_includes_default"
+if test "x$ac_cv_header_asm_hwcap_h" = xyes
+then :
+  printf '%s\n' "#define HAVE_ASM_HWCAP_H 1" >>confdefs.h
+
 fi
 ac_fn_c_check_header_compile "$LINENO" "asm/termbits.h" "ac_cv_header_asm_termbits_h" "$ac_includes_default"
 if test "x$ac_cv_header_asm_termbits_h" = xyes
@@ -10812,6 +10818,12 @@ if test "x$ac_cv_header_bluetooth_bluetooth_h" = xyes
 then :
   printf '%s\n' "#define HAVE_BLUETOOTH_BLUETOOTH_H 1" >>confdefs.h
 
+fi
+ac_fn_c_check_header_compile "$LINENO" "dev/evdev/input.h" "ac_cv_header_dev_evdev_input_h" "$ac_includes_default"
+if test "x$ac_cv_header_dev_evdev_input_h" = xyes
+then :
+  printf '%s\n' "#define HAVE_DEV_EVDEV_INPUT_H 1" >>confdefs.h
+
 fi
 ac_fn_c_check_header_compile "$LINENO" "elf.h" "ac_cv_header_elf_h" "$ac_includes_default"
 if test "x$ac_cv_header_elf_h" = xyes
@@ -13150,7 +13162,14 @@ if test "x$ac_cv_cflags__static_pie" = xyes
 then :
   as_fn_append WINEPRELOADER_LDFLAGS " -static-pie"
 else case e in #(
-  e) as_fn_append WINEPRELOADER_LDFLAGS " -static -Wl,-Ttext=0x7d7d00000000" ;;
+  e)
+                case $HOST_ARCH in
+                  aarch64)
+                                                            as_fn_append WINEPRELOADER_LDFLAGS " -static -Wl,-Ttext=0x7d7d000000" ;;
+                  *)
+                    as_fn_append WINEPRELOADER_LDFLAGS " -static -Wl,-Ttext=0x7d7d00000000" ;;
+                esac
+             ;;
 esac
 fi
             ;;
@@ -17257,7 +17276,10 @@ if test "x$ac_cv_func_SCardEstablishContext" = xyes
 then :
   :
 else case e in #(
-  e) PCSCLITE_LIBS="" ;;
+  e) case $host_os in
+                        darwin*) PCSCLITE_LIBS="-framework PCSC" ;;
+                        *) PCSCLITE_LIBS="";;
+                     esac ;;
 esac
 fi
 
@@ -23942,7 +23964,7 @@ cat >>"$CONFIG_STATUS" <<\_ACEOF || ac_write_fail=1
 # report actual input values of CONFIG_FILES etc. instead of their
 # values after options handling.
 ac_log="
-This file was extended by Wine $as_me 11.12, which was
+This file was extended by Wine $as_me 11.13, which was
 generated by GNU Autoconf 2.73.  Invocation command line was
 
   CONFIG_FILES    = $CONFIG_FILES
@@ -24002,7 +24024,7 @@ ac_cs_config_escaped=`printf '%s\n' "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
 cat >>"$CONFIG_STATUS" <<_ACEOF || ac_write_fail=1
 ac_cs_config='$ac_cs_config_escaped'
 ac_cs_version="\\
-Wine config.status 11.12
+Wine config.status 11.13
 configured by $0, generated by GNU Autoconf 2.73,
   with options \\"\$ac_cs_config\\"
 
diff --git a/configure.ac b/configure.ac
index a8e3bf77076f..ee0b1c483d47 100644
--- a/configure.ac
+++ b/configure.ac
@@ -726,10 +726,12 @@ AC_CHECK_HEADERS(\
 	OpenCL/opencl.h \
 	arpa/inet.h \
 	arpa/nameser.h \
+	asm/hwcap.h \
 	asm/termbits.h \
 	asm/types.h \
 	asm/user.h \
-        bluetooth/bluetooth.h \
+	bluetooth/bluetooth.h \
+	dev/evdev/input.h \
 	elf.h \
 	gettext-po.h \
 	link.h \
@@ -1104,7 +1106,16 @@ case $host_os in
           *)
             WINE_TRY_CFLAGS([-static-pie],
                             [AS_VAR_APPEND([WINEPRELOADER_LDFLAGS],[" -static-pie"])],
-                            [AS_VAR_APPEND([WINEPRELOADER_LDFLAGS],[" -static -Wl,-Ttext=0x7d7d00000000"])])
+            [
+                case $HOST_ARCH in
+                  aarch64)
+                    dnl AArch64 kernels may be limited to 39 bits of address
+                    dnl space; limit the text section load address to that.
+                    AS_VAR_APPEND([WINEPRELOADER_LDFLAGS],[" -static -Wl,-Ttext=0x7d7d000000"]) ;;
+                  *)
+                    AS_VAR_APPEND([WINEPRELOADER_LDFLAGS],[" -static -Wl,-Ttext=0x7d7d00000000"]) ;;
+                esac
+            ])
             ;;
         esac
         ;;
@@ -1511,10 +1522,11 @@ dnl **** Check for libpcsclite ****
 if test "x$with_pcsclite" != "xno"
 then
     WINE_PACKAGE_FLAGS(PCSCLITE,[libpcsclite],[-lpcsclite],,,
-                 [AC_CHECK_FUNC(SCardEstablishContext,[:],[PCSCLITE_LIBS=""])],
-                 [case $host_os in
-                    darwin*) AC_SUBST(PCSCLITE_LIBS,"-framework PCSC") ;;
-                  esac])
+                 [AC_CHECK_FUNC(SCardEstablishContext,[:],
+                    [case $host_os in
+                        darwin*) PCSCLITE_LIBS="-framework PCSC" ;;
+                        *) PCSCLITE_LIBS="";;
+                     esac])])
 fi
 WINE_NOTICE_WITH(pcsclite,[test "x$PCSCLITE_LIBS" = x],
                  [libpcsclite not found, smart cards won't be supported.],
diff --git a/dlls/acledit/Makefile.in b/dlls/acledit/Makefile.in
index dc396f3e385a..fe62cc1f57bf 100644
--- a/dlls/acledit/Makefile.in
+++ b/dlls/acledit/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = acledit.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	main.c
diff --git a/dlls/avrt/Makefile.in b/dlls/avrt/Makefile.in
index 19ce3034a989..a76c61ab953f 100644
--- a/dlls/avrt/Makefile.in
+++ b/dlls/avrt/Makefile.in
@@ -2,7 +2,5 @@ MODULE    = avrt.dll
 IMPORTLIB = avrt
 IMPORTS   = user32
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	main.c
diff --git a/dlls/cfgmgr32/cfgmgr32.c b/dlls/cfgmgr32/cfgmgr32.c
index c53cf69bfac6..ce5747338cff 100644
--- a/dlls/cfgmgr32/cfgmgr32.c
+++ b/dlls/cfgmgr32/cfgmgr32.c
@@ -1868,7 +1868,8 @@ CONFIGRET WINAPI CM_Get_Device_ID_ExW( DEVINST node, WCHAR *buffer, ULONG len, U
     if (*dev.instance) path_len += swprintf( path + path_len, ARRAY_SIZE(path) - path_len, L"\\%s", dev.instance );
 
     if (path_len > len) return CR_BUFFER_SMALL;
-    memcpy( buffer, path, (path_len + 1) * sizeof(WCHAR) );
+    memcpy( buffer, path, path_len * sizeof(WCHAR) );
+    if (path_len < len) buffer[path_len] = 0;
 
     return CR_SUCCESS;
 }
@@ -1881,9 +1882,9 @@ CONFIGRET WINAPI CM_Get_Device_ID_ExA( DEVINST node, char *bufferA, ULONG len, U
     WCHAR *bufferW;
     CONFIGRET ret;
 
-    bufferW = bufferA ? malloc( len * sizeof(WCHAR) ) : NULL;
-    ret = CM_Get_Device_ID_ExW( node, bufferA ? bufferW : NULL, len, flags, machine );
-    if (!ret && bufferA && len && !WideCharToMultiByte( CP_ACP, 0, bufferW, len, bufferA, len, 0, 0 ))
+    bufferW = bufferA ? malloc( (len + 1) * sizeof(WCHAR) ) : NULL;
+    ret = CM_Get_Device_ID_ExW( node, bufferA ? bufferW : NULL, len + 1, flags, machine );
+    if (!ret && bufferA && len && !WideCharToMultiByte( CP_ACP, 0, bufferW, -1, bufferA, len, 0, 0 ))
     {
         if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) ret = CR_BUFFER_SMALL;
         else ret = CR_FAILURE;
diff --git a/dlls/cfgmgr32/tests/cfgmgr32.c b/dlls/cfgmgr32/tests/cfgmgr32.c
index 213eb7f62297..1062874fb730 100644
--- a/dlls/cfgmgr32/tests/cfgmgr32.c
+++ b/dlls/cfgmgr32/tests/cfgmgr32.c
@@ -2905,6 +2905,7 @@ static void test_CM_Open_Device_Interface_Key(void)
 static void test_CM_Locate_DevNode(void)
 {
     WCHAR iface[4096], path[MAX_PATH], instance_id[MAX_PATH];
+    char pathA[MAX_PATH], instance_idA[MAX_PATH];
     DEVINST node, root = 0;
     DWORD size, type, len;
     CONFIGRET ret;
@@ -3000,10 +3001,28 @@ static void test_CM_Locate_DevNode(void)
     todo_wine ok_x4( ret, ==, CR_SUCCESS );
     todo_wine ok_wcs( L"HTREE\\ROOT\\0", path );
 
+    len = wcslen( instance_id );
     memset( path, 0xcd, sizeof(path) );
-    ret = CM_Get_Device_IDW( node, path, ARRAY_SIZE(path), 0 );
+    ret = CM_Get_Device_IDW( node, path, len, 0 );
+    ok_x4( ret, ==, CR_SUCCESS );
+    ok_x4( path[len], ==, 0xcdcd );
+
+    memset( path, 0xcd, sizeof(path) );
+    ret = CM_Get_Device_IDW( node, path, len + 1, 0 );
     ok_x4( ret, ==, CR_SUCCESS );
     ok_wcs( instance_id, path );
+
+    WideCharToMultiByte( CP_ACP, 0, instance_id, -1, instance_idA, ARRAY_SIZE(instance_idA), NULL, NULL );
+
+    len = strlen( instance_idA );
+    memset( path, 0xcd, sizeof(path) );
+    ret = CM_Get_Device_IDA( node, (char *)pathA, len, 0 );
+    ok_x4( ret, ==, CR_BUFFER_SMALL );
+
+    memset( pathA, 0xcd, sizeof(pathA) );
+    ret = CM_Get_Device_IDA( node, (char *)pathA, len + 1, 0 );
+    ok_x4( ret, ==, CR_SUCCESS );
+    ok_str( instance_idA, pathA );
 }
 
 static void test_CM_Open_DevNode_Key(void)
diff --git a/dlls/comctl32/tests/treeview.c b/dlls/comctl32/tests/treeview.c
index 997efa01a9df..bec52c810c83 100644
--- a/dlls/comctl32/tests/treeview.c
+++ b/dlls/comctl32/tests/treeview.c
@@ -423,9 +423,67 @@ static const struct message parent_right_click_seq[] = {
     { 0 }
 };
 
+static const struct message parent_itemchange[] = {
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
+    { WM_CTLCOLOREDIT, sent|optional },
+    { WM_CTLCOLOREDIT, sent|optional },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
+    { WM_CTLCOLOREDIT, sent|optional },
+    { WM_CTLCOLOREDIT, sent|optional },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { 0 }
+};
+
+/*
+* TVN_ITEMCHANGINGW and TVN_ITEMCHANGEDW intended.
+* TVN_ITEMCHANGINGA and TVN_ITEMCHANGEDA are never actually sent.
+*/
+static const struct message parent_itemchange_v6[] = {
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGINGA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGINGW },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMCHANGEDW },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDINGA },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_ITEMEXPANDEDA },
+    { WM_NOTIFY, sent|id, 0, 0, TVN_SELCHANGEDA },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { WM_NOTIFY, sent|id|optional, 0, 0, NM_CUSTOMDRAW },
+    { 0 }
+};
+
 static HWND hMainWnd;
 
-static HTREEITEM hRoot, hChild;
+static HTREEITEM hRoot, hChild, hBlockChange;
 
 static int pos = 0;
 static char sequence[256];
@@ -745,6 +803,55 @@ static void test_select(void)
     DestroyWindow(hTree);
 }
 
+static void test_itemchanging(void)
+{
+    BOOL r;
+    HWND hTree;
+
+    hTree = create_treeview_control(0);
+    fill_tree(hTree);
+
+    /* Verify that TVN_ITEMCHANGINGW and TVN_ITEMCHANGEDW are sent if v6 and not sent otherwise. */
+    flush_sequences(sequences, NUM_MSG_SEQUENCES);
+    SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
+    SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
+
+    if (g_v6) {
+        ok_sequence(sequences, PARENT_SEQ_INDEX, parent_itemchange_v6, "select item parent sequence v6", FALSE);
+    } else {
+        ok_sequence(sequences, PARENT_SEQ_INDEX, parent_itemchange, "select item parent sequence", FALSE);
+    }
+
+    /* Test if we can prevent item being selected; TVN_ITEMCHANGING logic only in v6 */
+    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hRoot);
+    expect(TRUE, r);
+    r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED) & TVIS_SELECTED;
+    expect(TVIS_SELECTED, r);
+    hBlockChange = hChild;
+    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
+    expect(TRUE, r);
+    r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hChild, TVIS_SELECTED) & TVIS_SELECTED;
+    if (g_v6)
+        expect(0, r);
+    else
+        expect(TVIS_SELECTED, r);
+
+    r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED) & TVIS_SELECTED;
+    expect(0, r);
+    hBlockChange = NULL;
+    r = SendMessageA(hTree, TVM_SELECTITEM, TVGN_CARET, (LPARAM)hChild);
+    expect(TRUE, r);
+    r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hChild, TVIS_SELECTED) & TVIS_SELECTED;
+    if (g_v6)
+        expect(0, r);
+    else
+        expect(TVIS_SELECTED, r);
+    r = SendMessageA(hTree, TVM_GETITEMSTATE, (WPARAM)hRoot, TVIS_SELECTED) & TVIS_SELECTED;
+    expect(0, r);
+
+    DestroyWindow(hTree);
+}
+
 static void test_getitemtext(void)
 {
     TVINSERTSTRUCTA ins;
@@ -1318,6 +1425,17 @@ static LRESULT CALLBACK parent_wnd_proc(HWND hWnd, UINT message, WPARAM wParam,
             NMTREEVIEWA *pTreeView = (LPNMTREEVIEWA) lParam;
             switch(pHdr->code)
             {
+                /*
+                 * TVN_ITEMCHANGINGW (not TVN_ITEMCHANGINGA) intended.
+                 * MS implementation appears to send TVN_ITEMCHANGINGW only.
+                 * Available only in comctl32 v6.
+                 */
+            case TVN_ITEMCHANGINGW:
+                {
+                    NMTVITEMCHANGE * pChange = (NMTVITEMCHANGE*) lParam;
+                    if (pChange->hItem == hBlockChange) return TRUE;
+                }
+                break;
             case TVN_SELCHANGINGA:
                 AddItem('(');
                 IdentifyItem(pTreeView->itemOld.hItem);
@@ -3411,6 +3529,7 @@ START_TEST(treeview)
     test_TVM_SORTCHILDREN();
     test_right_click();
     test_treeview_delete_midclick();
+    test_itemchanging();
 
     if (!load_v6_module(&ctx_cookie, &hCtx))
     {
@@ -3447,6 +3566,7 @@ START_TEST(treeview)
     test_TVS_FULLROWSELECT();
     test_TVM_SORTCHILDREN();
     test_treeview_delete_midclick();
+    test_itemchanging();
 
     unload_v6_module(ctx_cookie, hCtx);
 }
diff --git a/dlls/comctl32/treeview.c b/dlls/comctl32/treeview.c
index 6332040b7f57..d5418934eb2f 100644
--- a/dlls/comctl32/treeview.c
+++ b/dlls/comctl32/treeview.c
@@ -517,6 +517,55 @@ TREEVIEW_SendRealNotify(const TREEVIEW_INFO *infoPtr, UINT code, NMHDR *hdr)
     return SendMessageW(infoPtr->hwndNotify, WM_NOTIFY, hdr->idFrom, (LPARAM)hdr);
 }
 
+static BOOL TREEVIEW_SendItemChanging(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, UINT uChanged, UINT uStateOld, UINT uStateNew)
+{
+#if __WINE_COMCTL32_VERSION == 6
+    NMTVITEMCHANGE change;
+    change.uChanged = uChanged;
+    change.hItem = item;
+    change.uStateOld = uStateOld;
+    change.uStateNew = uStateNew;
+    change.lParam = item->lParam;
+    return TREEVIEW_SendRealNotify(infoPtr, TVN_ITEMCHANGINGW, &change.hdr);
+#endif
+    return FALSE;
+}
+
+static BOOL TREEVIEW_SendItemChanged(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, UINT uChanged, UINT uStateOld, UINT uStateNew)
+{
+#if __WINE_COMCTL32_VERSION == 6
+    NMTVITEMCHANGE change;
+    change.uChanged = uChanged;
+    change.hItem = item;
+    change.uStateOld = uStateOld;
+    change.uStateNew = uStateNew;
+    change.lParam = item->lParam;
+    return TREEVIEW_SendRealNotify(infoPtr, TVN_ITEMCHANGEDW, &change.hdr);
+#endif
+    return FALSE;
+}
+
+/*
+ * Change state helper
+ * Returns a boolean value indicating whether the change was accepted (always TRUE for legacy commoncontrols)
+ */
+static BOOL TREEVIEW_ChangeItemState(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item, UINT stateNew)
+{
+    UINT state = item->state;
+    if (state != stateNew)
+    {
+        if (TREEVIEW_SendItemChanging(infoPtr, item, TVIF_STATE, state, stateNew)) return FALSE;
+        item->state = stateNew;
+        if (TREEVIEW_SendItemChanged(infoPtr, item, TVIF_STATE, state, stateNew))
+        {
+            /* roll back */
+            item->state = state;
+            return FALSE;
+        }
+    }
+    return TRUE;
+}
+
 /*
  * Returns TRUE if the TREEVIEW is still valid after the notify.
  * The notification result is stored in *result if non-NULL.
@@ -1212,10 +1261,11 @@ TREEVIEW_DoSetItemT(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item,
 
     if (tvItem->mask & TVIF_STATE)
     {
-	TRACE("prevstate 0x%x, state 0x%x, mask 0x%x\n", item->state, tvItem->state,
+        UINT stateNew;
+        TRACE("prevstate 0x%x, state 0x%x, mask 0x%x\n", item->state, tvItem->state,
 	      tvItem->stateMask);
-	item->state &= ~tvItem->stateMask;
-	item->state |= (tvItem->state & tvItem->stateMask);
+        stateNew = (item->state & ~tvItem->stateMask) | (tvItem->state & tvItem->stateMask);
+        TREEVIEW_ChangeItemState(infoPtr, item, stateNew);
     }
 
     if (tvItem->mask & TVIF_STATEEX)
@@ -2338,7 +2388,7 @@ TREEVIEW_GetCount(const TREEVIEW_INFO *infoPtr)
 }
 
 static VOID
-TREEVIEW_ToggleItemState(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
+TREEVIEW_ToggleItemState(TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
 {
     if (infoPtr->dwStyle & TVS_CHECKBOXES)
     {
@@ -2364,8 +2414,10 @@ TREEVIEW_ToggleItemState(const TREEVIEW_INFO *infoPtr, TREEVIEW_ITEM *item)
 
         TRACE("stateImage: 0x%x\n", stateImage);
 
-        item->state = state;
-        TREEVIEW_Invalidate(infoPtr, item);
+        if (state != item->state && TREEVIEW_ChangeItemState(infoPtr, item, state))
+        {
+            TREEVIEW_Invalidate(infoPtr, item);
+        }
     }
 }
 
@@ -4598,10 +4650,11 @@ TREEVIEW_DoSelectItem(TREEVIEW_INFO *infoPtr, INT action, HTREEITEM newSelect,
 					newSelect))
 	    return FALSE;
 
-	if (prevSelect)
-	    prevSelect->state &= ~TVIS_SELECTED;
-	if (newSelect)
-	    newSelect->state |= TVIS_SELECTED;
+	if (prevSelect == NULL || TREEVIEW_ChangeItemState( infoPtr, prevSelect, prevSelect->state & ~TVIS_SELECTED))
+    {
+        /* deselected previous */
+        if (newSelect != NULL) TREEVIEW_ChangeItemState( infoPtr, newSelect, newSelect->state | TVIS_SELECTED);
+    }
 
 	infoPtr->selectedItem = newSelect;
 
diff --git a/dlls/connect/Makefile.in b/dlls/connect/Makefile.in
index e9518ca32739..9e5a89f4906f 100644
--- a/dlls/connect/Makefile.in
+++ b/dlls/connect/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = connect.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	main.c
diff --git a/dlls/crypt32/Makefile.in b/dlls/crypt32/Makefile.in
index 9af443bfb36d..9a0254150aa5 100644
--- a/dlls/crypt32/Makefile.in
+++ b/dlls/crypt32/Makefile.in
@@ -3,7 +3,7 @@ MODULE    = crypt32.dll
 UNIXLIB   = crypt32.so
 IMPORTLIB = crypt32
 IMPORTS   = user32 advapi32 bcrypt ncrypt
-DELAYIMPORTS = cryptnet
+DELAYIMPORTS = cryptnet rpcrt4
 UNIX_LIBS    = $(SECURITY_LIBS)
 UNIX_CFLAGS  = $(GNUTLS_CFLAGS)
 
diff --git a/dlls/crypt32/cert.c b/dlls/crypt32/cert.c
index c37432822a20..19a3e482c166 100644
--- a/dlls/crypt32/cert.c
+++ b/dlls/crypt32/cert.c
@@ -3759,53 +3759,37 @@ static void CRYPT_MakeCertInfo(PCERT_INFO info, const CRYPT_DATA_BLOB *pSerialNu
     }
 }
  
-typedef RPC_STATUS (RPC_ENTRY *UuidCreateFunc)(UUID *);
-typedef RPC_STATUS (RPC_ENTRY *UuidToStringFunc)(UUID *, unsigned char **);
-typedef RPC_STATUS (RPC_ENTRY *RpcStringFreeFunc)(unsigned char **);
-
-static HCRYPTPROV CRYPT_CreateKeyProv(void)
+WCHAR *CRYPT32_AllocateUniqueContainerName(void)
 {
-    HCRYPTPROV hProv = 0;
-    HMODULE rpcrt = LoadLibraryW(L"rpcrt4");
+    UUID uuid;
+    RPC_WSTR uuid_str = NULL;
+    WCHAR *ret = NULL;
+    RPC_STATUS status;
 
-    if (rpcrt)
-    {
-        UuidCreateFunc uuidCreate = (UuidCreateFunc)GetProcAddress(rpcrt,
-         "UuidCreate");
-        UuidToStringFunc uuidToString = (UuidToStringFunc)GetProcAddress(rpcrt,
-         "UuidToStringA");
-        RpcStringFreeFunc rpcStringFree = (RpcStringFreeFunc)GetProcAddress(
-         rpcrt, "RpcStringFreeA");
-
-        if (uuidCreate && uuidToString && rpcStringFree)
-        {
-            UUID uuid;
-            RPC_STATUS status = uuidCreate(&uuid);
+    status = UuidCreate( &uuid );
+    if (status != RPC_S_OK && status != RPC_S_UUID_LOCAL_ONLY) return NULL;
+    if (UuidToStringW( &uuid, &uuid_str ) != RPC_S_OK) return NULL;
 
-            if (status == RPC_S_OK || status == RPC_S_UUID_LOCAL_ONLY)
-            {
-                unsigned char *uuidStr;
+    if ((ret = CryptMemAlloc( (lstrlenW( (WCHAR *)uuid_str ) + 1) * sizeof(WCHAR) )))
+        lstrcpyW( ret, (WCHAR *)uuid_str );
+    RpcStringFreeW( &uuid_str );
+    return ret;
+}
 
-                status = uuidToString(&uuid, &uuidStr);
-                if (status == RPC_S_OK)
-                {
-                    BOOL ret = CryptAcquireContextA(&hProv, (LPCSTR)uuidStr,
-                     MS_DEF_PROV_A, PROV_RSA_FULL, CRYPT_NEWKEYSET);
+static HCRYPTPROV CRYPT_CreateKeyProv(void)
+{
+    HCRYPTPROV hProv = 0;
+    WCHAR *container = CRYPT32_AllocateUniqueContainerName();
 
-                    if (ret)
-                    {
-                        HCRYPTKEY key;
+    if (!container) return 0;
 
-                        ret = CryptGenKey(hProv, AT_SIGNATURE, 0, &key);
-                        if (ret)
-                            CryptDestroyKey(key);
-                    }
-                    rpcStringFree(&uuidStr);
-                }
-            }
-        }
-        FreeLibrary(rpcrt);
+    if (CryptAcquireContextW( &hProv, container, MS_DEF_PROV_W, PROV_RSA_FULL, CRYPT_NEWKEYSET ))
+    {
+        HCRYPTKEY key;
+        if (CryptGenKey( hProv, AT_SIGNATURE, 0, &key ))
+            CryptDestroyKey( key );
     }
+    CryptMemFree( container );
     return hProv;
 }
 
diff --git a/dlls/crypt32/crypt32_private.h b/dlls/crypt32/crypt32_private.h
index 7b6bdfa9df77..9de0c9f95482 100644
--- a/dlls/crypt32/crypt32_private.h
+++ b/dlls/crypt32/crypt32_private.h
@@ -26,6 +26,11 @@ BOOL CNG_ImportPubKey(CERT_PUBLIC_KEY_INFO *pubKeyInfo, BCRYPT_KEY_HANDLE *key);
 BOOL cng_prepare_signature(const char *alg_oid, BYTE *encoded_sig, DWORD encoded_sig_len,
     BYTE **sig_value, DWORD *sig_len);
 
+/* Returns a freshly-allocated, NUL-terminated UUID string suitable for use
+ * as a CSP key container name. Caller frees with CryptMemFree.  Returns
+ * NULL on failure. */
+WCHAR *CRYPT32_AllocateUniqueContainerName(void);
+
 /* a few asn.1 tags we need */
 #define ASN_BOOL            (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x01)
 #define ASN_BITSTRING       (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x03)
diff --git a/dlls/crypt32/pfx.c b/dlls/crypt32/pfx.c
index b7419e4f446e..81f0faebd2b5 100644
--- a/dlls/crypt32/pfx.c
+++ b/dlls/crypt32/pfx.c
@@ -38,20 +38,23 @@ static HCRYPTPROV import_key( cert_store_data_t data, DWORD flags )
     DWORD size, acquire_flags;
     void *key;
     struct import_store_key_params params = { data, NULL, &size };
+    /* Use a unique container name per import. With a NULL container +
+     * CRYPT_NEWKEYSET the CSP falls back to its per-user default container
+     * ("<USERNAME>"), so a second PFX import overwrites the first import's
+     * private key inside that shared container; any cert from the first
+     * import still held will then dereference (via CRYPT_KEY_PROV_INFO)
+     * to the second import's key. */
+    WCHAR *container = CRYPT32_AllocateUniqueContainerName();
 
-    if (CRYPT32_CALL( import_store_key, &params ) != STATUS_BUFFER_TOO_SMALL) return 0;
+    if (!container) return 0;
+
+    if (CRYPT32_CALL( import_store_key, &params ) != STATUS_BUFFER_TOO_SMALL) goto done;
 
     acquire_flags = (flags & CRYPT_MACHINE_KEYSET) | CRYPT_NEWKEYSET;
-    if (!CryptAcquireContextW( &prov, NULL, MS_ENHANCED_PROV_W, PROV_RSA_FULL, acquire_flags ))
+    if (!CryptAcquireContextW( &prov, container, MS_ENH_RSA_AES_PROV_W, PROV_RSA_AES, acquire_flags ))
     {
-        if (GetLastError() != NTE_EXISTS) return 0;
-
-        acquire_flags &= ~CRYPT_NEWKEYSET;
-        if (!CryptAcquireContextW( &prov, NULL, MS_ENHANCED_PROV_W, PROV_RSA_FULL, acquire_flags ))
-        {
-            WARN( "CryptAcquireContextW failed %08lx\n", GetLastError() );
-            return 0;
-        }
+        WARN( "CryptAcquireContextW failed %08lx\n", GetLastError() );
+        goto done;
     }
 
     params.buf = key = CryptMemAlloc( size );
@@ -60,11 +63,15 @@ static HCRYPTPROV import_key( cert_store_data_t data, DWORD flags )
     {
         WARN( "CryptImportKey failed %08lx\n", GetLastError() );
         CryptReleaseContext( prov, 0 );
+        prov = 0;
         CryptMemFree( key );
-        return 0;
+        goto done;
     }
     CryptDestroyKey( cryptkey );
     CryptMemFree( key );
+
+done:
+    CryptMemFree( container );
     return prov;
 }
 
diff --git a/dlls/crypt32/tests/store.c b/dlls/crypt32/tests/store.c
index 10b861675ca8..4240fd339a39 100644
--- a/dlls/crypt32/tests/store.c
+++ b/dlls/crypt32/tests/store.c
@@ -3541,6 +3541,112 @@ static void test_PFXImportCertStore(void)
     CertCloseStore( store, 0 );
 }
 
+static void test_PFXImportCertStore_unique_containers(void)
+{
+    /* Two persistent (CRYPT_USER_KEYSET) PFX imports of the *same* PKCS#12
+     * blob must land in two *distinct* CSP key containers; otherwise the
+     * second import overwrites the first import's private key inside the
+     * shared container, and any cert still held from the first import
+     * then dereferences (via CRYPT_KEY_PROV_INFO) to the second import's
+     * key. The container-name format is internal to the importer; only
+     * its uniqueness across calls is observable to applications, and that
+     * uniqueness is what this test checks. */
+
+    CRYPT_DATA_BLOB pfx = { sizeof(pfxdata), (BYTE *)pfxdata };
+    BYTE buf1[512], buf2[512];
+    CRYPT_KEY_PROV_INFO *info1 = (CRYPT_KEY_PROV_INFO *)buf1;
+    CRYPT_KEY_PROV_INFO *info2 = (CRYPT_KEY_PROV_INFO *)buf2;
+    HCERTSTORE store1, store2;
+    const CERT_CONTEXT *cert1, *cert2;
+    DWORD size;
+    BOOL ret;
+
+    store1 = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE | CRYPT_USER_KEYSET );
+    ok( store1 != NULL, "first PFXImportCertStore failed: %lu\n", GetLastError() );
+    store2 = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE | CRYPT_USER_KEYSET );
+    ok( store2 != NULL, "second PFXImportCertStore failed: %lu\n", GetLastError() );
+    if (!store1 || !store2) goto done;
+
+    cert1 = CertFindCertificateInStore( store1, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL );
+    cert2 = CertFindCertificateInStore( store2, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL );
+    ok( cert1 != NULL, "cert1 lookup failed: %08lx\n", GetLastError() );
+    ok( cert2 != NULL, "cert2 lookup failed: %08lx\n", GetLastError() );
+    if (!cert1 || !cert2) goto done_close;
+
+    size = sizeof(buf1);
+    ret = CertGetCertificateContextProperty( cert1, CERT_KEY_PROV_INFO_PROP_ID, info1, &size );
+    ok( ret, "cert1 has no KEY_PROV_INFO: %08lx\n", GetLastError() );
+    size = sizeof(buf2);
+    ret = CertGetCertificateContextProperty( cert2, CERT_KEY_PROV_INFO_PROP_ID, info2, &size );
+    ok( ret, "cert2 has no KEY_PROV_INFO: %08lx\n", GetLastError() );
+    if (!info1->pwszContainerName || !info2->pwszContainerName) goto done_certs;
+
+    ok( wcscmp( info1->pwszContainerName, info2->pwszContainerName ) != 0,
+        "two PFX imports collided into the same container %s — the second "
+        "import would have overwritten the first cert's private key\n",
+        wine_dbgstr_w( info1->pwszContainerName ) );
+
+done_certs:
+    CertFreeCertificateContext( cert1 );
+    CertFreeCertificateContext( cert2 );
+done_close:
+    CertCloseStore( store1, 0 );
+    CertCloseStore( store2, 0 );
+done:
+    ;
+}
+
+static void test_PFXImportCertStore_sha256_signing(void)
+{
+    CRYPT_DATA_BLOB pfx = { sizeof(pfxdata), (BYTE *)pfxdata };
+    BYTE buf[512];
+    CRYPT_KEY_PROV_INFO *info = (CRYPT_KEY_PROV_INFO *)buf;
+    HCERTSTORE store;
+    const CERT_CONTEXT *cert;
+    HCRYPTPROV prov;
+    HCRYPTHASH hash;
+    DWORD size;
+    BOOL ret;
+
+    store = PFXImportCertStore( &pfx, NULL, CRYPT_EXPORTABLE | CRYPT_USER_KEYSET );
+    ok( store != NULL, "PFXImportCertStore failed: %lu\n", GetLastError() );
+    if (!store) return;
+
+    cert = CertFindCertificateInStore( store, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL );
+    ok( cert != NULL, "no cert in store: %08lx\n", GetLastError() );
+    if (!cert) goto done_close;
+
+    size = sizeof(buf);
+    ret = CertGetCertificateContextProperty( cert, CERT_KEY_PROV_INFO_PROP_ID, info, &size );
+    ok( ret, "cert has no KEY_PROV_INFO: %08lx\n", GetLastError() );
+    if (!ret) goto done_cert;
+
+    ok( info->dwProvType == PROV_RSA_AES,
+        "PFX-imported key sits in provider type %lu, expected PROV_RSA_AES (%u) so that "
+        "SHA-256 signing works; PROV_RSA_FULL (1) can only hash MD5 and SHA-1\n",
+        info->dwProvType, PROV_RSA_AES );
+
+    if (info->dwProvType == PROV_RSA_AES)
+    {
+        ret = CryptAcquireContextW( &prov, info->pwszContainerName, info->pwszProvName,
+                                    info->dwProvType, 0 );
+        ok( ret, "CryptAcquireContextW(PROV_RSA_AES) failed: %08lx\n", GetLastError() );
+        if (ret)
+        {
+            ret = CryptCreateHash( prov, CALG_SHA_256, 0, 0, &hash );
+            ok( ret, "CryptCreateHash(CALG_SHA_256) failed on PFX-imported key's CSP: %08lx "
+                "— this is what NTE_BAD_ALGID looks like from userspace\n", GetLastError() );
+            if (ret) CryptDestroyHash( hash );
+            CryptReleaseContext( prov, 0 );
+        }
+    }
+
+done_cert:
+    CertFreeCertificateContext( cert );
+done_close:
+    CertCloseStore( store, 0 );
+}
+
 static void test_PFXExportCertStoreEx(void)
 {
     HCERTSTORE store, store2;
@@ -3769,6 +3875,8 @@ START_TEST(store)
 
     test_I_UpdateStore();
     test_PFXImportCertStore();
+    test_PFXImportCertStore_unique_containers();
+    test_PFXImportCertStore_sha256_signing();
     test_PFXExportCertStoreEx();
     test_CryptQueryObject();
 }
diff --git a/dlls/d3d11/tests/d3d11.c b/dlls/d3d11/tests/d3d11.c
index 00ec3b0d2b9c..78012054bff7 100644
--- a/dlls/d3d11/tests/d3d11.c
+++ b/dlls/d3d11/tests/d3d11.c
@@ -36384,9 +36384,9 @@ static void test_nv12(void)
     static const float clear_values_float[4] = {100.0, 100.0, 100.0, 100.0};
 
     static const char cs_code[] =
-            "Texture2D<uint> luma : register(t0);\n"
-            "Texture2D<uint2> chroma : register(t1);\n"
-            "RWTexture2D<uint> check : register(u1);\n"
+            "Texture2D<float> luma : register(t0);\n"
+            "Texture2D<float2> chroma : register(t1);\n"
+            "RWTexture2D<float> check : register(u1);\n"
             "\n"
             "uint2 size;\n"
             "\n"
@@ -36564,7 +36564,7 @@ static void test_nv12(void)
         ok(hr == S_OK, "Got hr %#lx.\n", hr);
 
         desc.Height += height / 2;
-        desc.Format = DXGI_FORMAT_R8_UINT;
+        desc.Format = DXGI_FORMAT_R8_UNORM;
         desc.BindFlags = D3D11_BIND_UNORDERED_ACCESS;
 
         hr = ID3D11Device_CreateTexture2D(device, &desc, NULL, &check_texture);
@@ -36581,7 +36581,7 @@ static void test_nv12(void)
         hr = ID3D11Device_CreateShaderResourceView(device, (ID3D11Resource *)texture, &srv_desc, &srvs[0]);
         ok(hr == E_INVALIDARG, "Got hr %#lx.\n", hr);
 
-        srv_desc.Format = DXGI_FORMAT_R8_UINT;
+        srv_desc.Format = DXGI_FORMAT_R8_UNORM;
         srv_desc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
         srv_desc.Texture2D.MostDetailedMip = 0;
         srv_desc.Texture2D.MipLevels = 1;
@@ -36589,12 +36589,12 @@ static void test_nv12(void)
         hr = ID3D11Device_CreateShaderResourceView(device, (ID3D11Resource *)texture, &srv_desc, &srvs[0]);
         ok(hr == S_OK, "Got hr %#lx.\n", hr);
 
-        srv_desc.Format = DXGI_FORMAT_R8G8_UINT;
+        srv_desc.Format = DXGI_FORMAT_R8G8_UNORM;
 
         hr = ID3D11Device_CreateShaderResourceView(device, (ID3D11Resource *)texture, &srv_desc, &srvs[1]);
         ok(hr == S_OK, "Got hr %#lx.\n", hr);
 
-        uav_desc.Format = DXGI_FORMAT_R8_UINT;
+        uav_desc.Format = DXGI_FORMAT_R8_UNORM;
         uav_desc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
         uav_desc.Texture2D.MipSlice = 0;
 
diff --git a/dlls/dbghelp/dwarf.c b/dlls/dbghelp/dwarf.c
index c7ccc062221b..32ab0029ac0a 100644
--- a/dlls/dbghelp/dwarf.c
+++ b/dlls/dbghelp/dwarf.c
@@ -1327,6 +1327,11 @@ static const char* dwarf2_get_cpp_name(dwarf2_debug_info_t* di, const char* name
         if (!di->unit_ctx->cpp_name) return name;
     }
     last = di->unit_ctx->cpp_name + MAX_SYM_NAME - strlen(name) - 1;
+    if (last < di->unit_ctx->cpp_name)
+    {
+        WARN("Too long C++ qualified identifier for %s... using unqualified identifier\n", debugstr_a(name));
+        return name;
+    }
     strcpy(last, name);
 
     for (di = di->parent; di; di = di->parent)
diff --git a/dlls/dinput/tests/device8.c b/dlls/dinput/tests/device8.c
index 37ed94285614..ab26b18fb5d5 100644
--- a/dlls/dinput/tests/device8.c
+++ b/dlls/dinput/tests/device8.c
@@ -1937,6 +1937,7 @@ static void test_hid_touch_screen(void)
     RAWINPUTDEVICE rawdevice = {.usUsagePage = HID_USAGE_PAGE_DIGITIZER, .usUsage = HID_USAGE_DIGITIZER_TOUCH_SCREEN};
     UINT rawbuffer_count, rawbuffer_size, expect_flags, id, width, height;
     WCHAR device_path[MAX_PATH];
+    POINTER_INPUT_TYPE type;
     char rawbuffer[1024];
     RAWINPUT *rawinput;
     HANDLE file;
@@ -2022,6 +2023,9 @@ static void test_hid_touch_screen(void)
     todo_wine /* missing POINTER_MESSAGE_FLAG_FIRSTBUTTON */
     ok( HIWORD( pointer_wparam[0] ) == expect_flags, "got wparam %#Ix\n", pointer_wparam[0] );
     ok( LOWORD( pointer_wparam[0] ) > 0, "got wparam %#Ix\n", pointer_wparam[0] );
+    ret = GetPointerType( LOWORD( pointer_wparam[0] ), &type );
+    ok( ret, "GetPointerType failed, error %lu\n", GetLastError() );
+    ok( type == PT_TOUCH, "got pointer type %#lx\n", type );
     ok( LOWORD( pointer_lparam[0] ) * 128 / width == 0x08, "got lparam %#Ix\n", pointer_lparam[0] );
     ok( HIWORD( pointer_lparam[0] ) * 128 / height == 0x10, "got lparam %#Ix\n", pointer_lparam[0] );
     id = LOWORD( pointer_wparam[0] );
@@ -2165,6 +2169,9 @@ static void test_hid_touch_screen(void)
     todo_wine /* missing POINTER_MESSAGE_FLAG_FIRSTBUTTON */
     ok( HIWORD( pointer_wparam[0] ) == expect_flags, "got wparam %#Ix\n", pointer_wparam[0] );
     ok( LOWORD( pointer_wparam[0] ) > 0, "got wparam %#Ix\n", pointer_wparam[0] );
+    ret = GetPointerType( LOWORD( pointer_wparam[0] ), &type );
+    ok( ret, "GetPointerType failed, error %lu\n", GetLastError() );
+    ok( type == PT_TOUCH, "got pointer type %#lx\n", type );
     ok( LOWORD( pointer_lparam[0] ) * 128 / width == 0x08, "got lparam %#Ix\n", pointer_lparam[0] );
     ok( HIWORD( pointer_lparam[0] ) * 128 / height == 0x10, "got lparam %#Ix\n", pointer_lparam[0] );
     ok( pointer_wparam[1] == 0, "got wparam %#Ix\n", pointer_wparam[1] );
@@ -2189,6 +2196,9 @@ static void test_hid_touch_screen(void)
         broken(HIWORD( pointer_wparam[0] ) == (expect_flags & ~POINTER_MESSAGE_FLAG_CONFIDENCE)), /* Win8 32bit */
         "got wparam %#Ix\n", pointer_wparam[0] );
     ok( LOWORD( pointer_wparam[0] ) == id, "got wparam %#Ix\n", pointer_wparam[0] );
+    ret = GetPointerType( LOWORD( pointer_wparam[0] ), &type );
+    ok( ret, "GetPointerType failed, error %lu\n", GetLastError() );
+    ok( type == PT_TOUCH, "got pointer type %#lx\n", type );
     ok( LOWORD( pointer_lparam[0] ) * 128 / width == 0x08, "got lparam %#Ix\n", pointer_lparam[0] );
     ok( HIWORD( pointer_lparam[0] ) * 128 / height == 0x10, "got lparam %#Ix\n", pointer_lparam[0] );
     ok( pointer_wparam[1] == 0, "got wparam %#Ix\n", pointer_wparam[1] );
@@ -2215,6 +2225,9 @@ static void test_hid_touch_screen(void)
     todo_wine /* missing POINTER_MESSAGE_FLAG_FIRSTBUTTON */
     ok( HIWORD( pointer_wparam[0] ) == expect_flags, "got wparam %#Ix\n", pointer_wparam[0] );
     ok( LOWORD( pointer_wparam[0] ) > 0, "got wparam %#Ix\n", pointer_wparam[0] );
+    ret = GetPointerType( LOWORD( pointer_wparam[0] ), &type );
+    ok( ret, "GetPointerType failed, error %lu\n", GetLastError() );
+    ok( type == PT_TOUCH, "got pointer type %#lx\n", type );
     ok( LOWORD( pointer_lparam[0] ) * 128 / width == 0x08, "got lparam %#Ix\n", pointer_lparam[0] );
     ok( HIWORD( pointer_lparam[0] ) * 128 / height == 0x10, "got lparam %#Ix\n", pointer_lparam[0] );
     id = LOWORD( pointer_wparam[0] );
@@ -2226,6 +2239,9 @@ static void test_hid_touch_screen(void)
         broken(HIWORD( pointer_wparam[1] ) == (expect_flags & ~POINTER_MESSAGE_FLAG_CONFIDENCE)), /* Win8 32bit */
         "got wparam %#Ix\n", pointer_wparam[1] );
     ok( LOWORD( pointer_wparam[1] ) == id + 1, "got wparam %#Ix\n", pointer_wparam[1] );
+    ret = GetPointerType( LOWORD( pointer_wparam[1] ), &type );
+    ok( ret, "GetPointerType failed, error %lu\n", GetLastError() );
+    ok( type == PT_TOUCH, "got pointer type %#lx\n", type );
     ok( LOWORD( pointer_lparam[1] ) * 128 / width == 0x18, "got lparam %#Ix\n", pointer_lparam[1] );
     ok( HIWORD( pointer_lparam[1] ) * 128 / height == 0x20, "got lparam %#Ix\n", pointer_lparam[1] );
 
@@ -2249,6 +2265,9 @@ static void test_hid_touch_screen(void)
         broken(HIWORD( pointer_wparam[0] ) == (expect_flags & ~POINTER_MESSAGE_FLAG_CONFIDENCE)), /* Win8 32bit */
         "got wparam %#Ix\n", pointer_wparam[0] );
     ok( LOWORD( pointer_wparam[0] ) == id, "got wparam %#Ix\n", pointer_wparam[0] );
+    ret = GetPointerType( LOWORD( pointer_wparam[0] ), &type );
+    ok( ret, "GetPointerType failed, error %lu\n", GetLastError() );
+    ok( type == PT_TOUCH, "got pointer type %#lx\n", type );
     ok( LOWORD( pointer_lparam[0] ) * 128 / width == 0x08, "got lparam %#Ix\n", pointer_lparam[0] );
     ok( HIWORD( pointer_lparam[0] ) * 128 / height == 0x10, "got lparam %#Ix\n", pointer_lparam[0] );
 
@@ -2257,6 +2276,9 @@ static void test_hid_touch_screen(void)
         broken(HIWORD( pointer_wparam[1] ) == (expect_flags & ~POINTER_MESSAGE_FLAG_CONFIDENCE)), /* Win8 32bit */
         "got wparam %#Ix\n", pointer_wparam[1] );
     ok( LOWORD( pointer_wparam[1] ) == id + 1, "got wparam %#Ix\n", pointer_wparam[1] );
+    ret = GetPointerType( LOWORD( pointer_wparam[1] ), &type );
+    ok( ret, "GetPointerType failed, error %lu\n", GetLastError() );
+    ok( type == PT_TOUCH, "got pointer type %#lx\n", type );
     ok( LOWORD( pointer_lparam[1] ) * 128 / width == 0x18, "got lparam %#Ix\n", pointer_lparam[1] );
     ok( HIWORD( pointer_lparam[1] ) * 128 / height == 0x20, "got lparam %#Ix\n", pointer_lparam[1] );
 
@@ -2486,7 +2508,7 @@ static void test_scan_codes( IDirectInputDevice8W *device, HANDLE event, HWND hw
             hr = IDirectInputDevice8_GetProperty( device, DIPROP_SCANCODE, &prop_dword.diph );
 
             if (!map[j].found)
-                todo_wine ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_SCANCODE returned %#lx\n", hr );
+                ok( hr == DIERR_NOTFOUND, "GetProperty DIPROP_SCANCODE returned %#lx\n", hr );
             else if (version < 0x0800)
                 ok( hr == DIERR_UNSUPPORTED, "GetProperty DIPROP_SCANCODE returned %#lx\n", hr );
             else
diff --git a/dlls/dinput/tests/hid.c b/dlls/dinput/tests/hid.c
index 7c42ac54f748..dfd35156f22d 100644
--- a/dlls/dinput/tests/hid.c
+++ b/dlls/dinput/tests/hid.c
@@ -1427,7 +1427,7 @@ static void test_write_file( HANDLE file, int report_id, ULONG report_len )
         .report_id = report_id,
         .report_len = report_len - (report_id ? 0 : 1),
         .report_buf = {report_id ? report_id : 0xcd,0xcd,0xcd,0xcd,0xcd},
-        .ret_length = 3,
+        .ret_length = 5,
         .ret_status = STATUS_SUCCESS,
     };
 
@@ -1463,16 +1463,8 @@ static void test_write_file( HANDLE file, int report_id, ULONG report_len )
         ret = WriteFile( file, report, report_len, &length, NULL );
     }
 
-    if (report_id)
-    {
-        ok( ret, "WriteFile failed, last error %lu\n", GetLastError() );
-        ok( length == 2, "WriteFile wrote %lu\n", length );
-    }
-    else
-    {
-        ok( ret, "WriteFile failed, last error %lu\n", GetLastError() );
-        ok( length == 3, "WriteFile wrote %lu\n", length );
-    }
+    ok( ret, "WriteFile failed, last error %lu\n", GetLastError() );
+    ok( length == report_len, "WriteFile wrote %lu\n", length );
 
     set_hid_expect( file, NULL, 0 );
 }
diff --git a/dlls/dmsynth/synthsink.c b/dlls/dmsynth/synthsink.c
index 0257bc08d036..fe2a1c6cd441 100644
--- a/dlls/dmsynth/synthsink.c
+++ b/dlls/dmsynth/synthsink.c
@@ -27,6 +27,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(dmsynth);
 
 #define BUFFER_SUBDIVISIONS 10
 #define WRITE_PERIOD 20
+#define JITTER_BUFFER 10
 
 struct synth_sink
 {
@@ -102,7 +103,8 @@ static HRESULT synth_sink_wait_write(struct synth_sink *sink, IDirectSoundBuffer
     EnterCriticalSection(&sink->cs);
     estimated_play_pos = (sink->play_pos + (master_time - sink->play_pos_time)
             * format->nSamplesPerSec / 10000000ll * format->nBlockAlign) % caps->dwBufferBytes;
-    write_latency = (sink->write_pos - sink->play_pos + caps->dwBufferBytes) % caps->dwBufferBytes;
+    write_latency = (sink->write_pos - sink->play_pos + caps->dwBufferBytes) % caps->dwBufferBytes
+            + JITTER_BUFFER * format->nSamplesPerSec / 1000 * format->nBlockAlign;
     current_offset = (sink->written - estimated_play_pos + caps->dwBufferBytes) % caps->dwBufferBytes;
     LeaveCriticalSection(&sink->cs);
 
@@ -190,7 +192,8 @@ static HRESULT synth_sink_wait_play_end(struct synth_sink *sink, IDirectSoundBuf
             return hr;
 
         EnterCriticalSection(&sink->cs);
-        write_latency = (sink->write_pos - sink->play_pos + caps->dwBufferBytes) % caps->dwBufferBytes;
+        write_latency = (sink->write_pos - sink->play_pos + caps->dwBufferBytes) % caps->dwBufferBytes
+                + JITTER_BUFFER * format->nSamplesPerSec / 1000 * format->nBlockAlign;
         LeaveCriticalSection(&sink->cs);
 
         if (sink->written >= written + write_latency)
diff --git a/dlls/dnsapi/dnsapi.spec b/dlls/dnsapi/dnsapi.spec
index a8ffa40a0ae5..50113eef89fb 100644
--- a/dlls/dnsapi/dnsapi.spec
+++ b/dlls/dnsapi/dnsapi.spec
@@ -112,6 +112,8 @@
 @ stub DnsServiceNotificationRegister_W
 @ stub DnsSetMaxNumberOfAddressesToRegister
 @ stub DnsStatusString
+@ stdcall DnsStartMulticastQuery(ptr ptr)
+@ stdcall DnsStopMulticastQuery(ptr)
 @ stub DnsStringCopyAllocateEx
 @ stub DnsUnicodeToUtf8
 @ stub DnsUpdate
diff --git a/dlls/dnsapi/main.c b/dlls/dnsapi/main.c
index 9b057a09a058..083334ecb834 100644
--- a/dlls/dnsapi/main.c
+++ b/dlls/dnsapi/main.c
@@ -243,3 +243,23 @@ DNS_STATUS WINAPI DnsServiceBrowse( PDNS_SERVICE_BROWSE_REQUEST request, PDNS_SE
     FIXME( "(%p, %p) stub\n", request, cancel );
     return ERROR_SUCCESS;
 }
+
+/******************************************************************************
+ * DnsStartMulticastQuery                  [DNSAPI.@]
+ *
+ */
+DNS_STATUS WINAPI DnsStartMulticastQuery(MDNS_QUERY_REQUEST *request, MDNS_QUERY_HANDLE *handle)
+{
+    FIXME( "(%p, %p) stub\n", request, handle );
+    return ERROR_SUCCESS;
+}
+
+/******************************************************************************
+ * DnsStopMulticastQuery                   [DNSAPI.@]
+ *
+ */
+DNS_STATUS WINAPI DnsStopMulticastQuery(MDNS_QUERY_HANDLE *handle)
+{
+    FIXME( "(%p) stub\n", handle );
+    return ERROR_SUCCESS;
+}
diff --git a/dlls/dplayx/dplay.c b/dlls/dplayx/dplay.c
index c646c48596ea..d963c030d6db 100644
--- a/dlls/dplayx/dplay.c
+++ b/dlls/dplayx/dplay.c
@@ -615,6 +615,7 @@ HRESULT DP_HandleMessage( IDirectPlayImpl *This, void *messageBody,
       TRACE( "GOT THE SELF MESSAGE: %p -> 0x%08lx\n", messageHeader, ((const DWORD *)messageHeader)[1] );
       NS_SetLocalAddr( This->dp2->lpNameServerData, messageHeader, 20 );
       DP_MSG_ReplyReceived( This, wCommandId, messageBody, dwMessageBodySize, messageHeader );
+      break;
 
     case DPMSGCMD_FORWARDADDPLAYER:
       TRACE( "Sending message to self to get my addr\n" );
diff --git a/dlls/dsound/dsound_convert.c b/dlls/dsound/dsound_convert.c
index c1acc95652c2..3160a4ddefae 100644
--- a/dlls/dsound/dsound_convert.c
+++ b/dlls/dsound/dsound_convert.c
@@ -47,68 +47,68 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(dsound);
 
-#ifdef WORDS_BIGENDIAN
-#define le16(x) RtlUshortByteSwap((x))
-#define le32(x) RtlUlongByteSwap((x))
-#else
-#define le16(x) (x)
-#define le32(x) (x)
-#endif
-
-static float get8(const IDirectSoundBufferImpl *dsb, BYTE *base, DWORD channel)
+static void get8(const IDirectSoundBufferImpl *dsb, BYTE *base, float *dst, unsigned samples, DWORD channel)
 {
+    DWORD channels = dsb->pwfx->nChannels;
     const BYTE *buf = base + channel;
-    return (buf[0] - 0x80) / (float)0x80;
+    int i;
+
+    for (i = 0; i < samples; ++i)
+        dst[i] = (buf[i * channels] - 0x80) / (float)0x80;
 }
 
-static float get16(const IDirectSoundBufferImpl *dsb, BYTE *base, DWORD channel)
+static void get16(const IDirectSoundBufferImpl *dsb, BYTE *base, float *dst, unsigned samples, DWORD channel)
 {
+    DWORD channels = dsb->pwfx->nChannels;
     const BYTE *buf = base + 2 * channel;
     const SHORT *sbuf = (const SHORT*)(buf);
-    SHORT sample = (SHORT)le16(*sbuf);
-    return sample / (float)0x8000;
+    int i;
+
+    for (i = 0; i < samples; ++i)
+        dst[i] = sbuf[i * channels] / (float)0x8000;
 }
 
-static float get24(const IDirectSoundBufferImpl *dsb, BYTE *base, DWORD channel)
+static void get24(const IDirectSoundBufferImpl *dsb, BYTE *base, float *dst, unsigned samples, DWORD channel)
 {
-    LONG sample;
+    DWORD channels = dsb->pwfx->nChannels;
     const BYTE *buf = base + 3 * channel;
-
-    /* The next expression deliberately has an overflow for buf[2] >= 0x80,
-       this is how negative values are made.
-     */
-    sample = (buf[0] << 8) | (buf[1] << 16) | (buf[2] << 24);
-    return sample / (float)0x80000000U;
+    int i;
+
+    for (i = 0; i < samples; ++i) {
+        /* The next expression deliberately has an overflow for buf[2] >= 0x80,
+           this is how negative values are made.
+         */
+        LONG sample =
+                (buf[i * channels * 3 + 0] << 8) |
+                (buf[i * channels * 3 + 1] << 16) |
+                (buf[i * channels * 3 + 2] << 24);
+        dst[i] = sample / (float)0x80000000U;
+    }
 }
 
-static float get32(const IDirectSoundBufferImpl *dsb, BYTE *base, DWORD channel)
+static void get32(const IDirectSoundBufferImpl *dsb, BYTE *base, float *dst, unsigned samples, DWORD channel)
 {
+    DWORD channels = dsb->pwfx->nChannels;
     const BYTE *buf = base + 4 * channel;
     const LONG *sbuf = (const LONG*)(buf);
-    LONG sample = le32(*sbuf);
-    return sample / (float)0x80000000U;
-}
+    int i;
 
-static float getieee32(const IDirectSoundBufferImpl *dsb, BYTE *base, DWORD channel)
-{
-    const BYTE *buf = base + 4 * channel;
-    const float *sbuf = (const float*)(buf);
-    /* The value will be clipped later, when put into some non-float buffer */
-    return *sbuf;
+    for (i = 0; i < samples; ++i)
+        dst[i] = sbuf[i * channels] / (float)0x80000000U;
 }
 
-const bitsgetfunc getbpp[5] = {get8, get16, get24, get32, getieee32};
+const bitsgetfunc getbpp[4] = {get8, get16, get24, get32};
 
-float get_mono(const IDirectSoundBufferImpl *dsb, BYTE *base, DWORD channel)
+void getieee32(const IDirectSoundBufferImpl *dsb, BYTE *base, float *dst, unsigned samples, DWORD channel)
 {
     DWORD channels = dsb->pwfx->nChannels;
-    DWORD c;
-    float val = 0;
-    /* XXX: does Windows include LFE into the mix? */
-    for (c = 0; c < channels; c++)
-        val += dsb->get_aux(dsb, base, c);
-    val /= channels;
-    return val;
+    const BYTE *buf = base + 4 * channel;
+    const float *sbuf = (const float*)(buf);
+    int i;
+
+    for (i = 0; i < samples; ++i)
+        /* The value will be clipped later, when put into some non-float buffer */
+        dst[i] = sbuf[i * channels];
 }
 
 void putieee32(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value)
@@ -174,6 +174,12 @@ void put_stereo2surround51(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD c
     }
 }
 
+void put_mono(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value)
+{
+    /* XXX: does Windows include LFE into the mix? */
+    dsb->put_aux(dsb, pos, 0, value);
+}
+
 void put_surround512stereo(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value)
 {
     /* based on analyzing a recording of a dsound downmix */
diff --git a/dlls/dsound/dsound_private.h b/dlls/dsound/dsound_private.h
index c9b8a1b202d8..fc6fbfd1cc57 100644
--- a/dlls/dsound/dsound_private.h
+++ b/dlls/dsound/dsound_private.h
@@ -43,9 +43,10 @@ typedef struct IDirectSoundBufferImpl        IDirectSoundBufferImpl;
 typedef struct DirectSoundDevice             DirectSoundDevice;
 
 /* dsound_convert.h */
-typedef float (*bitsgetfunc)(const IDirectSoundBufferImpl *, BYTE *, DWORD);
-typedef void (*bitsputfunc)(const IDirectSoundBufferImpl *, DWORD, DWORD, float);
-extern const bitsgetfunc getbpp[5];
+typedef void (*bitsgetfunc)(const IDirectSoundBufferImpl *dsb, BYTE *base, float *dst, unsigned samples, DWORD channel);
+typedef void (*bitsputfunc)(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
+extern const bitsgetfunc getbpp[4];
+void getieee32(const IDirectSoundBufferImpl *dsb, BYTE *base, float *dst, unsigned samples, DWORD channel);
 void putieee32(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
 void putieee32_sum(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
 void mixieee32(float *src, float *dst, unsigned samples);
@@ -161,7 +162,7 @@ struct IDirectSoundBufferImpl
     BOOL                        ds3db_need_recalc;
     /* Used for bit depth conversion */
     int                         mix_channels;
-    bitsgetfunc get, get_aux;
+    bitsgetfunc get;
     bitsputfunc put, put_aux;
     int                         num_filters;
     DSFilter*                   filters;
@@ -169,12 +170,12 @@ struct IDirectSoundBufferImpl
     struct list entry;
 };
 
-float get_mono(const IDirectSoundBufferImpl *dsb, BYTE *base, DWORD channel);
 void put_mono2stereo(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
 void put_mono2quad(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
 void put_stereo2quad(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
 void put_mono2surround51(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
 void put_stereo2surround51(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
+void put_mono(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
 void put_surround512stereo(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
 void put_surround712stereo(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
 void put_quad2stereo(const IDirectSoundBufferImpl *dsb, DWORD pos, DWORD channel, float value);
diff --git a/dlls/dsound/mixer.c b/dlls/dsound/mixer.c
index c8d0ea369d29..fca7f09d6b61 100644
--- a/dlls/dsound/mixer.c
+++ b/dlls/dsound/mixer.c
@@ -137,10 +137,9 @@ void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
 		dsb->freqAccNum = (dsb->freqAccNum * (LONG64)dsb->freqAdjustDen +
 				oldFreqAdjustDen / 2) / oldFreqAdjustDen;
 
-	dsb->get_aux = ieee ? getbpp[4] : getbpp[dsb->pwfx->wBitsPerSample/8 - 1];
 	dsb->put_aux = putieee32;
 
-	dsb->get = dsb->get_aux;
+	dsb->get = ieee ? getieee32 : getbpp[dsb->pwfx->wBitsPerSample/8 - 1];
 	dsb->put = dsb->put_aux;
 
 	if (ichannels == ochannels)
@@ -164,8 +163,9 @@ void DSOUND_RecalcFormat(IDirectSoundBufferImpl *dsb)
 	}
 	else if (ochannels == 1)
 	{
-		dsb->mix_channels = 1;
-		dsb->get = get_mono;
+		dsb->mix_channels = ichannels;
+		dsb->put = put_mono;
+		dsb->put_aux = putieee32_sum;
 	}
 	else if (ichannels == 2 && ochannels == 4)
 	{
@@ -279,12 +279,28 @@ void DSOUND_CheckEvent(const IDirectSoundBufferImpl *dsb, DWORD playpos, int len
     }
 }
 
-static inline float get_current_sample(const IDirectSoundBufferImpl *dsb,
-        BYTE *buffer, DWORD buflen, DWORD mixpos, DWORD channel)
+static inline void get_samples(const IDirectSoundBufferImpl *dsb, BYTE *buffer, DWORD buflen,
+        DWORD mixpos, DWORD channel, DWORD count, float *dst)
 {
-    if (mixpos >= buflen && !(dsb->playflags & DSBPLAY_LOOPING))
-        return 0.0f;
-    return dsb->get(dsb, buffer + (mixpos % buflen), channel);
+    UINT istride = dsb->pwfx->nBlockAlign;
+    DWORD advance;
+    DWORD pos;
+
+    if (!(dsb->playflags & DSBPLAY_LOOPING)) {
+        advance = buflen < mixpos ? 0 : min((buflen - mixpos) / istride, count);
+        dsb->get(dsb, buffer + mixpos, dst, advance, channel);
+        memset(dst + advance, 0, (count - advance) * sizeof(float));
+        return;
+    }
+
+    advance = min((buflen - mixpos % buflen) / istride, count);
+    dsb->get(dsb, buffer + mixpos % buflen, dst, advance, channel);
+    pos = advance;
+    while (pos < count) {
+        advance = min(buflen / istride, count - pos);
+        dsb->get(dsb, buffer, dst + pos, advance, channel);
+        pos += advance;
+    }
 }
 
 #ifdef __SSE__
@@ -536,7 +552,7 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, DWORD *f
     UINT required_input = max(
             (freqAcc_start + (count - 1) * dsb->freqAdjustNum) / dsb->freqAdjustDen + FIR_WIDTH,
             (freqAcc_start + (count - 1 + FIR_WIDTH) * dsb->freqAdjustNum) / dsb->freqAdjustDen);
-    float *intermediate, *output, *itmp;
+    float *intermediate, *output;
 
     DWORD len = required_input * channels;
     /* Allocate an output buffer for each channel with padding on both ends as
@@ -570,14 +586,14 @@ static UINT cp_fields_resample(IDirectSoundBufferImpl *dsb, UINT count, DWORD *f
      * if you want -msse3 to have any effect.
      * This is good for CPU cache effects, too.
      */
-    itmp = intermediate;
     for (channel = 0; channel < channels; channel++) {
-        for (i = 0; i < committed_samples; i++)
-            *(itmp++) = get_current_sample(dsb, dsb->committedbuff,
-                dsb->writelead, dsb->committed_mixpos + i * istride, channel);
-        for (; i < required_input; i++)
-            *(itmp++) = get_current_sample(dsb, dsb->buffer->memory,
-                    dsb->buflen, dsb->sec_mixpos + i * istride, channel);
+        get_samples(dsb, dsb->committedbuff, dsb->writelead, dsb->committed_mixpos, channel,
+                committed_samples, intermediate + channel * required_input);
+        if (required_input > committed_samples)
+            get_samples(dsb, dsb->buffer->memory, dsb->buflen,
+                    dsb->sec_mixpos + committed_samples * istride, channel,
+                    required_input - committed_samples,
+                    intermediate + channel * required_input + committed_samples);
     }
 
     for (channel = 0; channel < channels; channel++)
@@ -597,25 +613,43 @@ static UINT cp_fields_noresample(IDirectSoundBufferImpl *dsb, UINT count)
     UINT istride = dsb->pwfx->nBlockAlign;
     UINT ostride = dsb->device->pwfx->nChannels * sizeof(float);
     UINT committed_samples = 0;
+    float *intermediate;
     DWORD channel, i;
 
+    DWORD len = count * dsb->mix_channels;
+    len *= sizeof(float);
+
     if (!secondarybuffer_is_audible(dsb))
         return count;
 
+    if (!dsb->device->cp_buffer) {
+        dsb->device->cp_buffer = malloc(len);
+        dsb->device->cp_buffer_len = len;
+    } else if (len > dsb->device->cp_buffer_len) {
+        dsb->device->cp_buffer = realloc(dsb->device->cp_buffer, len);
+        dsb->device->cp_buffer_len = len;
+    }
+
+    intermediate = dsb->device->cp_buffer;
+
     if(dsb->use_committed) {
         committed_samples = (dsb->writelead - dsb->committed_mixpos) / istride;
         committed_samples = committed_samples <= count ? committed_samples : count;
     }
 
-    for (i = 0; i < committed_samples; i++)
-        for (channel = 0; channel < dsb->mix_channels; channel++)
-            dsb->put(dsb, i * ostride, channel, get_current_sample(dsb, dsb->committedbuff,
-                dsb->writelead, dsb->committed_mixpos + i * istride, channel));
+    for (channel = 0; channel < dsb->mix_channels; channel++)
+    {
+        get_samples(dsb, dsb->committedbuff, dsb->writelead, dsb->committed_mixpos, channel,
+                committed_samples, intermediate + channel * count);
+        if (count > committed_samples)
+            get_samples(dsb, dsb->buffer->memory, dsb->buflen,
+                    dsb->sec_mixpos + committed_samples * istride, channel,
+                    count - committed_samples, intermediate + channel * count + committed_samples);
+    }
 
-    for (; i < count; i++)
+    for (i = 0; i < count; i++)
         for (channel = 0; channel < dsb->mix_channels; channel++)
-            dsb->put(dsb, i * ostride, channel, get_current_sample(dsb, dsb->buffer->memory,
-                dsb->buflen, dsb->sec_mixpos + i * istride, channel));
+            dsb->put(dsb, i * ostride, channel, intermediate[channel * count + i]);
 
     return count;
 }
diff --git a/dlls/dsound/tests/dsound8.c b/dlls/dsound/tests/dsound8.c
index 1b363b9ad7b4..3660eaa15dfd 100644
--- a/dlls/dsound/tests/dsound8.c
+++ b/dlls/dsound/tests/dsound8.c
@@ -1232,7 +1232,7 @@ static IMediaObject testdmo;
 static IMediaObjectInPlace testdmo_inplace;
 static LONG testdmo_refcount;
 static WAVEFORMATEX testdmo_input_type;
-static BOOL testdmo_input_type_set, testdmo_output_type_set;
+static BOOL testdmo_input_type_set;
 
 static unsigned int got_Discontinuity;
 static HANDLE got_Process;
@@ -1339,8 +1339,6 @@ static HRESULT WINAPI dmo_SetOutputType(IMediaObject *iface, DWORD index, const
     ok(type->cbFormat == sizeof(WAVEFORMATEX), "Got format size %lu.\n", type->cbFormat);
 
     ok(!memcmp(type->pbFormat, &testdmo_input_type, sizeof(WAVEFORMATEX)), "Format blocks didn't match.\n");
-
-    testdmo_output_type_set = TRUE;
     return S_OK;
 }
 
diff --git a/dlls/dwrite/analyzer.c b/dlls/dwrite/analyzer.c
index c991b8d0d4be..3b3927a4d2b8 100644
--- a/dlls/dwrite/analyzer.c
+++ b/dlls/dwrite/analyzer.c
@@ -308,6 +308,12 @@ system_fallback_config[] =
     { "1C00-1C4F",              L"Noto Sans Lepcha" },
     { "1C50-1C7F",              L"Noto Sans Ol Chiki" },
 
+    /* Phonetic Extensions            - 1D00-1D7F
+       Phonetic Extensions Supplement - 1D80-1DBF
+       Latin Extended Additional      - 1E00-1EFF */
+    { "1D00-1D7F, 1D80-1DBF, "
+      "1E00-1EFF",              L"Noto Sans" },
+
     /* Arrows - 2190-21FF */
     { "2190-21FF",              L"Noto Sans Symbols" },
 
diff --git a/dlls/dxgi/device.c b/dlls/dxgi/device.c
index 46d252f2b671..eedcc81d845f 100644
--- a/dlls/dxgi/device.c
+++ b/dlls/dxgi/device.c
@@ -462,7 +462,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_swapchain_factory_create_swapchain(IWineDX
         return E_OUTOFMEMORY;
     }
 
-    if (FAILED(hr = d3d11_swapchain_init(object, device, &wined3d_desc)))
+    if (FAILED(hr = d3d11_swapchain_init(object, device, &wined3d_desc, fullscreen_desc)))
     {
         WARN("Failed to initialise swapchain, hr %#lx.\n", hr);
         free(object);
diff --git a/dlls/dxgi/dxgi_private.h b/dlls/dxgi/dxgi_private.h
index 483aac4d566b..cbe7c0981e64 100644
--- a/dlls/dxgi/dxgi_private.h
+++ b/dlls/dxgi/dxgi_private.h
@@ -180,19 +180,21 @@ struct d3d11_swapchain
     IWineDXGIDevice *device;
     IWineDXGIFactory *factory;
 
+    DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreen_desc;
     IDXGIOutput *target;
     LONG present_count;
     LONG in_set_fullscreen_state;
 };
 
 HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_device *device,
-        struct wined3d_swapchain_desc *desc);
+        struct wined3d_swapchain_desc *desc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc);
 
 HRESULT d3d12_swapchain_create(IWineDXGIFactory *factory, ID3D12CommandQueue *queue, HWND window,
         const DXGI_SWAP_CHAIN_DESC1 *swapchain_desc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc,
         IDXGISwapChain1 **swapchain);
 
 BOOL dxgi_validate_swapchain_desc(const DXGI_SWAP_CHAIN_DESC1 *desc);
+BOOL dxgi_validate_swapchain_fullscreen_desc(const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc);
 
 /* IDXGISurface/IDXGIResource */
 struct dxgi_resource
diff --git a/dlls/dxgi/factory.c b/dlls/dxgi/factory.c
index ebdb42a3806e..7837bffaca35 100644
--- a/dlls/dxgi/factory.c
+++ b/dlls/dxgi/factory.c
@@ -272,6 +272,7 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa
         const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc,
         IDXGIOutput *output, IDXGISwapChain1 **swapchain)
 {
+    DXGI_SWAP_CHAIN_FULLSCREEN_DESC windowed_fullscreen_desc = {0};
     IWineDXGISwapChainFactory *swapchain_factory;
     ID3D12CommandQueue *command_queue;
     HRESULT hr;
@@ -294,6 +295,14 @@ static HRESULT STDMETHODCALLTYPE dxgi_factory_CreateSwapChainForHwnd(IWineDXGIFa
     if (!dxgi_validate_swapchain_desc(desc))
         return DXGI_ERROR_INVALID_CALL;
 
+    if (!fullscreen_desc || !dxgi_validate_swapchain_fullscreen_desc(fullscreen_desc))
+    {
+        if (fullscreen_desc)
+            windowed_fullscreen_desc = *fullscreen_desc;
+        windowed_fullscreen_desc.Windowed = TRUE;
+        fullscreen_desc = &windowed_fullscreen_desc;
+    }
+
     if (output)
         FIXME("Ignoring output %p.\n", output);
 
diff --git a/dlls/dxgi/swapchain.c b/dlls/dxgi/swapchain.c
index 5c49c7f24820..eee4a2a42b33 100644
--- a/dlls/dxgi/swapchain.c
+++ b/dlls/dxgi/swapchain.c
@@ -100,6 +100,28 @@ BOOL dxgi_validate_swapchain_desc(const DXGI_SWAP_CHAIN_DESC1 *desc)
     return TRUE;
 }
 
+BOOL dxgi_validate_swapchain_fullscreen_desc(const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc)
+{
+    if (desc && !desc->Windowed)
+    {
+        if (!(desc->ScanlineOrdering >= DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED
+                && desc->ScanlineOrdering <= DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST))
+        {
+            WARN("Invalid scaline order %#x.\n", desc->ScanlineOrdering);
+            return FALSE;
+        }
+
+        if (!(desc->Scaling >= DXGI_MODE_SCALING_UNSPECIFIED
+                && desc->Scaling <= DXGI_MODE_SCALING_STRETCHED))
+        {
+            WARN("Invalid scaling %#x.\n", desc->Scaling);
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
 HRESULT dxgi_get_output_from_window(IWineDXGIFactory *factory, HWND window, IDXGIOutput **dxgi_output)
 {
     unsigned int adapter_idx, output_idx;
@@ -382,6 +404,15 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d11_swapchain_SetFullscreen
         return DXGI_ERROR_INVALID_CALL;
     }
 
+    if (fullscreen)
+    {
+        DXGI_SWAP_CHAIN_FULLSCREEN_DESC test_fullscreen_desc = swapchain->fullscreen_desc;
+
+        test_fullscreen_desc.Windowed = FALSE;
+        if (!dxgi_validate_swapchain_fullscreen_desc(&test_fullscreen_desc))
+            return DXGI_ERROR_INVALID_CALL;
+    }
+
     if (target)
     {
         IDXGIOutput_AddRef(target);
@@ -438,7 +469,8 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetFullscreenState(IDXGISwapCha
         BOOL *fullscreen, IDXGIOutput **target)
 {
     struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain4(iface);
-    struct wined3d_swapchain_desc swapchain_desc;
+    struct wined3d_swapchain_state *state;
+    BOOL windowed;
     HRESULT hr;
 
     TRACE("iface %p, fullscreen %p, target %p.\n", iface, fullscreen, target);
@@ -446,16 +478,17 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetFullscreenState(IDXGISwapCha
     if (fullscreen || target)
     {
         wined3d_mutex_lock();
-        wined3d_swapchain_get_desc(swapchain->wined3d_swapchain, &swapchain_desc);
+        state = wined3d_swapchain_get_state(swapchain->wined3d_swapchain);
+        windowed = wined3d_swapchain_state_is_windowed(state);
         wined3d_mutex_unlock();
     }
 
     if (fullscreen)
-        *fullscreen = !swapchain_desc.windowed;
+        *fullscreen = !windowed;
 
     if (target)
     {
-        if (!swapchain_desc.windowed)
+        if (!windowed)
         {
             if (!swapchain->target && FAILED(hr = IDXGISwapChain4_GetContainingOutput(iface, &swapchain->target)))
                 return hr;
@@ -648,7 +681,8 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetFullscreenDesc(IDXGISwapChai
         DXGI_SWAP_CHAIN_FULLSCREEN_DESC *desc)
 {
     struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain4(iface);
-    struct wined3d_swapchain_desc wined3d_desc;
+    struct wined3d_swapchain_state *state;
+    BOOL windowed;
 
     TRACE("iface %p, desc %p.\n", iface, desc);
 
@@ -659,16 +693,12 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetFullscreenDesc(IDXGISwapChai
     }
 
     wined3d_mutex_lock();
-    wined3d_swapchain_get_desc(swapchain->wined3d_swapchain, &wined3d_desc);
+    state = wined3d_swapchain_get_state(swapchain->wined3d_swapchain);
+    windowed = wined3d_swapchain_state_is_windowed(state);
     wined3d_mutex_unlock();
 
-    FIXME("Ignoring ScanlineOrdering and Scaling.\n");
-
-    desc->RefreshRate.Numerator = wined3d_desc.refresh_rate;
-    desc->RefreshRate.Denominator = 1;
-    desc->ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
-    desc->Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
-    desc->Windowed = wined3d_desc.windowed;
+    *desc = swapchain->fullscreen_desc;
+    desc->Windowed = windowed;
 
     return S_OK;
 }
@@ -781,23 +811,37 @@ static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetSourceSize(IDXGISwapChain4 *
 
 static HRESULT STDMETHODCALLTYPE d3d11_swapchain_SetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT max_latency)
 {
-    FIXME("iface %p, max_latency %u stub!\n", iface, max_latency);
+    struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain4(iface);
+    HRESULT hr;
 
-    return E_NOTIMPL;
+    TRACE("iface %p, max_latency %u.\n", iface, max_latency);
+
+    wined3d_mutex_lock();
+    hr = wined3d_swapchain_set_max_frame_latency(swapchain->wined3d_swapchain, max_latency);
+    wined3d_mutex_unlock();
+    return hr;
 }
 
 static HRESULT STDMETHODCALLTYPE d3d11_swapchain_GetMaximumFrameLatency(IDXGISwapChain4 *iface, UINT *max_latency)
 {
-    FIXME("iface %p, max_latency %p stub!\n", iface, max_latency);
+    struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain4(iface);
+    HRESULT hr;
 
-    return E_NOTIMPL;
+    TRACE("iface %p, max_latency %p.\n", iface, max_latency);
+
+    wined3d_mutex_lock();
+    hr = wined3d_swapchain_get_max_frame_latency(swapchain->wined3d_swapchain, max_latency);
+    wined3d_mutex_unlock();
+    return hr;
 }
 
 static HANDLE STDMETHODCALLTYPE d3d11_swapchain_GetFrameLatencyWaitableObject(IDXGISwapChain4 *iface)
 {
-    FIXME("iface %p stub!\n", iface);
+    struct d3d11_swapchain *swapchain = d3d11_swapchain_from_IDXGISwapChain4(iface);
 
-    return NULL;
+    TRACE("iface %p.\n", iface);
+
+    return wined3d_swapchain_get_frame_latency_waitable_object(swapchain->wined3d_swapchain);
 }
 
 static HRESULT STDMETHODCALLTYPE d3d11_swapchain_SetMatrixTransform(IDXGISwapChain4 *iface,
@@ -989,7 +1033,7 @@ static HRESULT d3d11_swapchain_create_d3d11_textures(struct d3d11_swapchain *swa
 }
 
 HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_device *device,
-        struct wined3d_swapchain_desc *desc)
+        struct wined3d_swapchain_desc *desc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc)
 {
     struct wined3d_swapchain_state *state;
     BOOL fullscreen;
@@ -1008,6 +1052,7 @@ HRESULT d3d11_swapchain_init(struct d3d11_swapchain *swapchain, struct dxgi_devi
 
     swapchain->IDXGISwapChain4_iface.lpVtbl = &d3d11_swapchain_vtbl;
     swapchain->state_parent.ops = &d3d11_swapchain_state_parent_ops;
+    swapchain->fullscreen_desc = *fullscreen_desc;
     swapchain->refcount = 1;
     wined3d_mutex_lock();
     wined3d_private_store_init(&swapchain->private_store);
@@ -2406,6 +2451,15 @@ static HRESULT STDMETHODCALLTYPE DECLSPEC_HOTPATCH d3d12_swapchain_SetFullscreen
         return DXGI_ERROR_INVALID_CALL;
     }
 
+    if (fullscreen)
+    {
+        DXGI_SWAP_CHAIN_FULLSCREEN_DESC test_fullscreen_desc = *fullscreen_desc;
+
+        test_fullscreen_desc.Windowed = FALSE;
+        if (!dxgi_validate_swapchain_fullscreen_desc(&test_fullscreen_desc))
+            return DXGI_ERROR_INVALID_CALL;
+    }
+
     if (target)
     {
         IDXGIOutput_AddRef(target);
@@ -3389,7 +3443,6 @@ HRESULT d3d12_swapchain_create(IWineDXGIFactory *factory, ID3D12CommandQueue *qu
         const DXGI_SWAP_CHAIN_DESC1 *swapchain_desc, const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *fullscreen_desc,
         IDXGISwapChain1 **swapchain)
 {
-    DXGI_SWAP_CHAIN_FULLSCREEN_DESC default_fullscreen_desc;
     struct D3D12_COMMAND_QUEUE_DESC queue_desc;
     struct d3d12_swapchain *object;
     ID3D12Device *device;
@@ -3402,13 +3455,6 @@ HRESULT d3d12_swapchain_create(IWineDXGIFactory *factory, ID3D12CommandQueue *qu
     if (queue_desc.Type != D3D12_COMMAND_LIST_TYPE_DIRECT)
         return DXGI_ERROR_INVALID_CALL;
 
-    if (!fullscreen_desc)
-    {
-        memset(&default_fullscreen_desc, 0, sizeof(default_fullscreen_desc));
-        default_fullscreen_desc.Windowed = TRUE;
-        fullscreen_desc = &default_fullscreen_desc;
-    }
-
     if (!(object = calloc(1, sizeof(*object))))
         return E_OUTOFMEMORY;
 
diff --git a/dlls/dxgi/tests/dxgi.c b/dlls/dxgi/tests/dxgi.c
index 0886adf76bd8..518fe47cbd8b 100644
--- a/dlls/dxgi/tests/dxgi.c
+++ b/dlls/dxgi/tests/dxgi.c
@@ -424,7 +424,7 @@ struct fullscreen_state
     RECT client_rect;
     HMONITOR monitor;
     RECT monitor_rect;
-    BOOL todo_style, todo_exstyle, todo_client_rect;
+    BOOL todo_style, todo_exstyle, todo_window_rect, todo_client_rect, todo_monitor_rect;
 };
 
 struct swapchain_fullscreen_state
@@ -468,6 +468,7 @@ static void check_fullscreen_state_(unsigned int line, const struct fullscreen_s
     ok_(__FILE__, line)((state->exstyle & ~WS_EX_TOPMOST) == (expected_state->exstyle & ~WS_EX_TOPMOST),
             "Got exstyle %#lx, expected %#lx.\n",
             state->exstyle & ~(DWORD)WS_EX_TOPMOST, expected_state->exstyle & ~(DWORD)WS_EX_TOPMOST);
+    todo_wine_if (expected_state->todo_window_rect)
     ok_(__FILE__, line)(EqualRect(&state->window_rect, &expected_state->window_rect),
             "Got window rect %s, expected %s.\n",
             wine_dbgstr_rect(&state->window_rect), wine_dbgstr_rect(&expected_state->window_rect));
@@ -478,6 +479,7 @@ static void check_fullscreen_state_(unsigned int line, const struct fullscreen_s
     ok_(__FILE__, line)(state->monitor == expected_state->monitor,
             "Got monitor %p, expected %p.\n",
             state->monitor, expected_state->monitor);
+    todo_wine_if (expected_state->todo_monitor_rect)
     ok_(__FILE__, line)(EqualRect(&state->monitor_rect, &expected_state->monitor_rect),
             "Got monitor rect %s, expected %s.\n",
             wine_dbgstr_rect(&state->monitor_rect), wine_dbgstr_rect(&expected_state->monitor_rect));
@@ -1739,21 +1741,21 @@ struct refresh_rates
     BOOL denominator_should_pass;
 };
 
-static void test_create_swapchain(void)
+static void test_create_swapchain(IUnknown *device, BOOL is_d3d12)
 {
     struct swapchain_fullscreen_state initial_state, expected_state;
     unsigned int  i, expected_width, expected_height;
     DXGI_SWAP_CHAIN_DESC creation_desc, result_desc;
     DXGI_SWAP_CHAIN_FULLSCREEN_DESC fullscreen_desc;
     DXGI_SWAP_CHAIN_DESC1 swapchain_desc;
-    IDXGIDevice *device, *bgra_device;
     ULONG refcount, expected_refcount;
     IUnknown *obj, *obj2, *parent;
+    IDXGIAdapter *adapter = NULL;
     IDXGISwapChain1 *swapchain1;
     RECT *expected_client_rect;
     IDXGISwapChain *swapchain;
+    IDXGIDevice *bgra_device;
     IDXGISurface1 *surface;
-    IDXGIAdapter *adapter;
     IDXGIFactory *factory;
     IDXGIOutput *target;
     BOOL fullscreen;
@@ -1769,11 +1771,7 @@ static void test_create_swapchain(void)
         { 0,  0,  TRUE, FALSE},
     };
 
-    if (!(device = create_device(0)))
-    {
-        skip("Failed to create device.\n");
-        return;
-    }
+    get_factory(device, is_d3d12, &factory);
 
     creation_desc.BufferDesc.Width = 800;
     creation_desc.BufferDesc.Height = 600;
@@ -1785,24 +1783,24 @@ static void test_create_swapchain(void)
     creation_desc.SampleDesc.Count = 1;
     creation_desc.SampleDesc.Quality = 0;
     creation_desc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
-    creation_desc.BufferCount = 1;
+    creation_desc.BufferCount = is_d3d12 ? 2 : 1;
     creation_desc.OutputWindow = NULL;
     creation_desc.Windowed = TRUE;
-    creation_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+    creation_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
     creation_desc.Flags = 0;
 
-    hr = IDXGIDevice_QueryInterface(device, &IID_IUnknown, (void **)&obj);
+    hr = IUnknown_QueryInterface(device, &IID_IUnknown, (void **)&obj);
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
 
-    hr = IDXGIDevice_GetAdapter(device, &adapter);
-    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
-
-    hr = IDXGIAdapter_GetParent(adapter, &IID_IDXGIFactory, (void **)&factory);
-    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    if (!is_d3d12)
+    {
+        hr = IDXGIDevice_GetAdapter((IDXGIDevice *)device, &adapter);
+        ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
 
-    expected_refcount = get_refcount(adapter);
+        expected_refcount = get_refcount(adapter);
+    }
     refcount = get_refcount(factory);
-    ok(refcount == 2, "Got unexpected refcount %lu.\n", refcount);
+    ok(refcount == (is_d3d12 ? 1 : 2), "Got unexpected refcount %lu.\n", refcount);
     refcount = get_refcount(device);
     ok(refcount == 2, "Got unexpected refcount %lu.\n", refcount);
 
@@ -1823,12 +1821,15 @@ static void test_create_swapchain(void)
     hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
 
-    refcount = get_refcount(adapter);
-    ok(refcount >= expected_refcount, "Got refcount %lu, expected >= %lu.\n", refcount, expected_refcount);
+    if (!is_d3d12)
+    {
+        refcount = get_refcount(adapter);
+        ok(refcount >= expected_refcount, "Got refcount %lu, expected >= %lu.\n", refcount, expected_refcount);
+    }
     refcount = get_refcount(factory);
-    todo_wine ok(refcount == 4, "Got unexpected refcount %lu.\n", refcount);
+    todo_wine ok(refcount == (is_d3d12 ? 5 : 4), "Got unexpected refcount %lu.\n", refcount);
     refcount = get_refcount(device);
-    ok(refcount == 3, "Got unexpected refcount %lu.\n", refcount);
+    todo_wine_if(is_d3d12) ok(refcount == (is_d3d12 ? 7 : 3), "Got unexpected refcount %lu.\n", refcount);
 
     hr = IDXGISwapChain_GetDesc(swapchain, NULL);
     ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr);
@@ -1837,13 +1838,13 @@ static void test_create_swapchain(void)
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
     ok(parent == (IUnknown *)factory, "Got unexpected parent interface pointer %p.\n", parent);
     refcount = IUnknown_Release(parent);
-    todo_wine ok(refcount == 4, "Got unexpected refcount %lu.\n", refcount);
+    todo_wine ok(refcount == (is_d3d12 ? 5 : 4), "Got unexpected refcount %lu.\n", refcount);
 
     hr = IDXGISwapChain_GetParent(swapchain, &IID_IDXGIFactory, (void **)&parent);
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
     ok(parent == (IUnknown *)factory, "Got unexpected parent interface pointer %p.\n", parent);
     refcount = IUnknown_Release(parent);
-    todo_wine ok(refcount == 4, "Got unexpected refcount %lu.\n", refcount);
+    todo_wine ok(refcount == (is_d3d12 ? 5 : 4), "Got unexpected refcount %lu.\n", refcount);
 
     hr = IDXGISwapChain_QueryInterface(swapchain, &IID_IDXGISwapChain1, (void **)&swapchain1);
     ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* Not available on all Windows versions. */,
@@ -1875,7 +1876,7 @@ static void test_create_swapchain(void)
     ok(!refcount, "Swapchain has %lu references left.\n", refcount);
 
     refcount = get_refcount(factory);
-    ok(refcount == 2, "Got unexpected refcount %lu.\n", refcount);
+    ok(refcount == (is_d3d12 ? 1 : 2), "Got unexpected refcount %lu.\n", refcount);
 
     for (i = 0; i < ARRAY_SIZE(refresh_list); ++i)
     {
@@ -1891,11 +1892,11 @@ static void test_create_swapchain(void)
         ok(result_desc.Windowed == creation_desc.Windowed, "Test %u: Got unexpected windowed %#x.\n",
                 i, result_desc.Windowed);
 
-        todo_wine_if (!refresh_list[i].numerator_should_pass)
+        todo_wine_if (!refresh_list[i].numerator_should_pass && !is_d3d12)
             ok(result_desc.BufferDesc.RefreshRate.Numerator == refresh_list[i].numerator,
                     "Numerator %u is %u.\n", i, result_desc.BufferDesc.RefreshRate.Numerator);
 
-        todo_wine_if (!refresh_list[i].denominator_should_pass)
+        todo_wine_if (!refresh_list[i].denominator_should_pass && !is_d3d12)
             ok(result_desc.BufferDesc.RefreshRate.Denominator == refresh_list[i].denominator,
                     "Denominator %u is %u.\n", i, result_desc.BufferDesc.RefreshRate.Denominator);
 
@@ -1989,11 +1990,11 @@ static void test_create_swapchain(void)
         if (result_desc.Windowed != creation_desc.Windowed)
             trace("Test %u: Failed to change fullscreen state.\n", i);
 
-        todo_wine_if (!refresh_list[i].numerator_should_pass)
+        todo_wine_if (!refresh_list[i].numerator_should_pass && !is_d3d12)
             ok(result_desc.BufferDesc.RefreshRate.Numerator == refresh_list[i].numerator,
                     "Numerator %u is %u.\n", i, result_desc.BufferDesc.RefreshRate.Numerator);
 
-        todo_wine_if (!refresh_list[i].denominator_should_pass)
+        todo_wine_if (!refresh_list[i].denominator_should_pass && !is_d3d12)
             ok(result_desc.BufferDesc.RefreshRate.Denominator == refresh_list[i].denominator,
                     "Denominator %u is %u.\n", i, result_desc.BufferDesc.RefreshRate.Denominator);
 
@@ -2013,9 +2014,10 @@ static void test_create_swapchain(void)
                     i, containing_output);
             IDXGIOutput_Release(containing_output);
 
-            ok(output_belongs_to_adapter(target, adapter),
-                    "Test %u: Output %p doesn't belong to adapter %p.\n",
-                    i, target, adapter);
+            if (adapter)
+                ok(output_belongs_to_adapter(target, adapter),
+                        "Test %u: Output %p doesn't belong to adapter %p.\n",
+                        i, target, adapter);
             IDXGIOutput_Release(target);
 
             hr = IDXGISwapChain_GetFullscreenState(swapchain, NULL, NULL);
@@ -2052,18 +2054,18 @@ static void test_create_swapchain(void)
     creation_desc.Windowed = TRUE;
     creation_desc.Flags = 0;
     hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
-    ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr);
+    ok(hr == (is_d3d12 ? DXGI_ERROR_INVALID_CALL : E_INVALIDARG), "Got unexpected hr %#lx.\n", hr);
 
     creation_desc.Windowed = FALSE;
     hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
-    ok(hr == E_INVALIDARG, "Got unexpected hr %#lx.\n", hr);
+    ok(hr == (is_d3d12 ? DXGI_ERROR_INVALID_CALL : E_INVALIDARG), "Got unexpected hr %#lx.\n", hr);
 
     creation_desc.BufferCount = 2;
     creation_desc.SwapEffect = DXGI_SWAP_EFFECT_FLIP_DISCARD;
     hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
     ok(hr == E_INVALIDARG || hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", hr);
-    creation_desc.BufferCount = 1;
-    creation_desc.SwapEffect = DXGI_SWAP_EFFECT_DISCARD;
+    creation_desc.BufferCount = is_d3d12 ? 2 : 1;
+    creation_desc.SwapEffect = is_d3d12 ? DXGI_SWAP_EFFECT_FLIP_DISCARD : DXGI_SWAP_EFFECT_DISCARD;
 
     check_window_fullscreen_state(creation_desc.OutputWindow, &initial_state.fullscreen_state);
 
@@ -2143,6 +2145,11 @@ static void test_create_swapchain(void)
     check_window_fullscreen_state(creation_desc.OutputWindow, &initial_state.fullscreen_state);
 
     /* Fullscreen */
+    /* Call flush_events() to make sure that the previously destroyed output windows are disappeared
+     * from the screen. Otherwise, creating a fullscreen swapchain returns DXGI_STATUS_OCCLUDED and
+     * gets a windowed swapchain instead. It's still possible that there is another window on the screen
+     * in the test environment so the following DXGI_STATUS_OCCLUDED return code is kept accepted */
+    flush_events();
     creation_desc.Windowed = FALSE;
     hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
     ok(hr == S_OK || hr == DXGI_STATUS_OCCLUDED, "Got unexpected hr %#lx.\n", hr);
@@ -2209,6 +2216,12 @@ static void test_create_swapchain(void)
             result_desc.BufferDesc.Width, expected_width);
     todo_wine ok(result_desc.BufferDesc.Height == expected_height, "Got height %u, expected %u.\n",
             result_desc.BufferDesc.Height, expected_height);
+    if (is_d3d12)
+    {
+        expected_state.fullscreen_state.todo_window_rect = TRUE;
+        expected_state.fullscreen_state.todo_client_rect = TRUE;
+        expected_state.fullscreen_state.todo_monitor_rect = TRUE;
+    }
     check_swapchain_fullscreen_state(swapchain, &expected_state);
     hr = IDXGISwapChain_SetFullscreenState(swapchain, FALSE, NULL);
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
@@ -2217,14 +2230,145 @@ static void test_create_swapchain(void)
 
     IDXGIOutput_Release(expected_state.target);
 
+    /* Try to create a fullscreen swapchain with an invalid scanline order. Expect swapchain
+     * creation successful but entering fullscreen mode fails */
+    creation_desc.BufferDesc.Width = 1024;
+    creation_desc.BufferDesc.Height = 768;
+    creation_desc.BufferDesc.RefreshRate.Numerator = 60;
+    creation_desc.BufferDesc.RefreshRate.Denominator = 1;
+    creation_desc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+    creation_desc.BufferDesc.ScanlineOrdering = 0xdeadbeef;
+    creation_desc.BufferDesc.Scaling = DXGI_MODE_SCALING_UNSPECIFIED;
+    creation_desc.Windowed = FALSE;
+    creation_desc.Flags = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
+    hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
+    ok(hr == DXGI_STATUS_OCCLUDED || hr == S_OK /* <= Win10 22H2 */, "Got unexpected hr %#lx.\n", hr);
+    hr = IDXGISwapChain_QueryInterface(swapchain, &IID_IDXGISwapChain1, (void **)&swapchain1);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* Not available on all Windows versions. */,
+            "Got unexpected hr %#lx.\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IDXGISwapChain1_GetFullscreenDesc(swapchain1, &fullscreen_desc);
+        ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+        ok(fullscreen_desc.ScanlineOrdering == creation_desc.BufferDesc.ScanlineOrdering,
+                "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering);
+        ok(fullscreen_desc.Scaling == creation_desc.BufferDesc.Scaling,
+                "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering);
+        ok(fullscreen_desc.Windowed != creation_desc.Windowed,
+                "Got unexpected windowed %#x.\n", fullscreen_desc.Windowed);
+        IDXGISwapChain1_Release(swapchain1);
+    }
+    hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    ok(result_desc.Windowed, "Got unexpected fullscreen state.\n");
+    hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    ok(!fullscreen, "Got unexpected fullscreen state.\n");
+    hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
+    ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", hr);
+    IDXGISwapChain_Release(swapchain);
+
+    /* Try to create a fullscreen swapchain with an invalid scanline order, zero width and height.
+     * Expect swapchain creation successful but entering fullscreen mode fails */
+    creation_desc.BufferDesc.Width = 0;
+    creation_desc.BufferDesc.Height = 0;
+    hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
+    ok(hr == DXGI_STATUS_OCCLUDED || hr == S_OK /* <= Win10 22H2 */, "Got unexpected hr %#lx.\n", hr);
+    hr = IDXGISwapChain_QueryInterface(swapchain, &IID_IDXGISwapChain1, (void **)&swapchain1);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* Not available on all Windows versions. */,
+            "Got unexpected hr %#lx.\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IDXGISwapChain1_GetFullscreenDesc(swapchain1, &fullscreen_desc);
+        ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+        ok(fullscreen_desc.ScanlineOrdering == creation_desc.BufferDesc.ScanlineOrdering,
+                "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering);
+        ok(fullscreen_desc.Scaling == creation_desc.BufferDesc.Scaling,
+                "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering);
+        ok(fullscreen_desc.Windowed != creation_desc.Windowed,
+                "Got unexpected windowed %#x.\n", fullscreen_desc.Windowed);
+        IDXGISwapChain1_Release(swapchain1);
+    }
+    hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    ok(result_desc.Windowed, "Got unexpected fullscreen state.\n");
+    hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    ok(!fullscreen, "Got unexpected fullscreen state.\n");
+    hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
+    ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", hr);
+    IDXGISwapChain_Release(swapchain);
+
+    /* Try to create a fullscreen swapchain with an invalid scaling. Expect swapchain creation
+     * successful but entering fullscreen mode fails */
+    creation_desc.BufferDesc.Width = 1024;
+    creation_desc.BufferDesc.Height = 768;
+    creation_desc.BufferDesc.ScanlineOrdering = DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED;
+    creation_desc.BufferDesc.Scaling = 0xdeadbeef;
+    hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
+    ok(hr == DXGI_STATUS_OCCLUDED || hr == S_OK /* <= Win10 22H2 */, "Got unexpected hr %#lx.\n", hr);
+    hr = IDXGISwapChain_QueryInterface(swapchain, &IID_IDXGISwapChain1, (void **)&swapchain1);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* Not available on all Windows versions. */,
+            "Got unexpected hr %#lx.\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IDXGISwapChain1_GetFullscreenDesc(swapchain1, &fullscreen_desc);
+        ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+        ok(fullscreen_desc.ScanlineOrdering == creation_desc.BufferDesc.ScanlineOrdering,
+                "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering);
+        ok(fullscreen_desc.Scaling == creation_desc.BufferDesc.Scaling,
+                "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering);
+        ok(fullscreen_desc.Windowed != creation_desc.Windowed,
+                "Got unexpected windowed %#x.\n", fullscreen_desc.Windowed);
+        IDXGISwapChain1_Release(swapchain1);
+    }
+    hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    ok(result_desc.Windowed, "Got unexpected fullscreen state.\n");
+    hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    ok(!fullscreen, "Got unexpected fullscreen state.\n");
+    hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
+    ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", hr);
+    IDXGISwapChain_Release(swapchain);
+
+    /* Try to create a fullscreen swapchain with an invalid scaling, zero width and height. Expect
+     * swapchain creation successful but entering fullscreen mode fails */
+    creation_desc.BufferDesc.Width = 0;
+    creation_desc.BufferDesc.Height = 0;
+    hr = IDXGIFactory_CreateSwapChain(factory, obj, &creation_desc, &swapchain);
+    ok(hr == DXGI_STATUS_OCCLUDED || hr == S_OK /* <= Win10 22H2 */, "Got unexpected hr %#lx.\n", hr);
+    hr = IDXGISwapChain_QueryInterface(swapchain, &IID_IDXGISwapChain1, (void **)&swapchain1);
+    ok(hr == S_OK || broken(hr == E_NOINTERFACE) /* Not available on all Windows versions. */,
+            "Got unexpected hr %#lx.\n", hr);
+    if (SUCCEEDED(hr))
+    {
+        hr = IDXGISwapChain1_GetFullscreenDesc(swapchain1, &fullscreen_desc);
+        ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+        ok(fullscreen_desc.ScanlineOrdering == creation_desc.BufferDesc.ScanlineOrdering,
+                "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering);
+        ok(fullscreen_desc.Scaling == creation_desc.BufferDesc.Scaling,
+                "Got unexpected scanline ordering %d.\n", fullscreen_desc.ScanlineOrdering);
+        ok(fullscreen_desc.Windowed != creation_desc.Windowed,
+                "Got unexpected windowed %#x.\n", fullscreen_desc.Windowed);
+        IDXGISwapChain1_Release(swapchain1);
+    }
+    hr = IDXGISwapChain_GetDesc(swapchain, &result_desc);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    ok(result_desc.Windowed, "Got unexpected fullscreen state.\n");
+    hr = IDXGISwapChain_GetFullscreenState(swapchain, &fullscreen, NULL);
+    ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
+    ok(!fullscreen, "Got unexpected fullscreen state.\n");
+    hr = IDXGISwapChain_SetFullscreenState(swapchain, TRUE, NULL);
+    ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", hr);
+    IDXGISwapChain_Release(swapchain);
+
 done:
     IUnknown_Release(obj);
-    refcount = IDXGIDevice_Release(device);
-    ok(!refcount, "Device has %lu references left.\n", refcount);
-    refcount = IDXGIAdapter_Release(adapter);
-    ok(!refcount, "Adapter has %lu references left.\n", refcount);
+    if (adapter)
+        IDXGIAdapter_Release(adapter);
     refcount = IDXGIFactory_Release(factory);
-    ok(!refcount, "Factory has %lu references left.\n", refcount);
+    ok(refcount == !is_d3d12, "Got unexpected refcount %lu.\n", refcount);
     check_window_fullscreen_state(creation_desc.OutputWindow, &initial_state.fullscreen_state);
     DestroyWindow(creation_desc.OutputWindow);
 }
@@ -7348,83 +7492,61 @@ static void test_frame_latency_event(IUnknown *device, BOOL is_d3d12)
     IDXGISwapChain1_Release(swapchain1);
 
     semaphore = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain2);
-    todo_wine_if(!is_d3d12)
     ok(!!semaphore, "Got unexpected NULL semaphore.\n");
 
     /* a new duplicate handle is returned each time */
     semaphore2 = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain2);
-    todo_wine_if(!is_d3d12)
     ok(!!semaphore2, "Got unexpected NULL semaphore.\n");
-    todo_wine_if(!is_d3d12)
     ok(semaphore != semaphore2, "Got the same semaphore twice %p.\n", semaphore);
 
     ret = CloseHandle(semaphore);
-    todo_wine_if(!is_d3d12)
     ok(!!ret, "Failed to close handle, last error %lu.\n", GetLastError());
     ret = CloseHandle(semaphore2);
-    todo_wine_if(!is_d3d12)
     ok(!!ret, "Failed to close handle, last error %lu.\n", GetLastError());
 
     semaphore = IDXGISwapChain2_GetFrameLatencyWaitableObject(swapchain2);
-    todo_wine_if(!is_d3d12)
     ok(!!semaphore, "Got unexpected NULL semaphore.\n");
 
     wait_result = WaitForSingleObject(semaphore, 0);
-    todo_wine_if(!is_d3d12)
     ok(!wait_result, "Got unexpected wait result %#lx.\n", wait_result);
     wait_result = WaitForSingleObject(semaphore, 0);
-    todo_wine_if(!is_d3d12)
     ok(wait_result == WAIT_TIMEOUT, "Got unexpected wait result %#lx.\n", wait_result);
 
     hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency);
-    todo_wine_if(!is_d3d12)
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
-    todo_wine_if(!is_d3d12)
     ok(frame_latency == 1, "Got unexpected frame latency %#x.\n", frame_latency);
 
     hr = IDXGISwapChain2_SetMaximumFrameLatency(swapchain2, 0);
     todo_wine_if(!is_d3d12)
     ok(hr == DXGI_ERROR_INVALID_CALL, "Got unexpected hr %#lx.\n", hr);
     hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency);
-    todo_wine_if(!is_d3d12)
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
-    todo_wine_if(!is_d3d12)
     ok(frame_latency == 1, "Got unexpected frame latency %#x.\n", frame_latency);
 
     /* raising the maximum frame latency releases the semaphore the
      * corresponding number of times */
     hr = IDXGISwapChain2_SetMaximumFrameLatency(swapchain2, 3);
-    todo_wine_if(!is_d3d12)
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
     hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency);
-    todo_wine_if(!is_d3d12)
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
-    todo_wine_if(!is_d3d12)
     ok(frame_latency == 3, "Got unexpected frame latency %#x.\n", frame_latency);
 
     wait_result = WaitForSingleObject(semaphore, 0);
-    todo_wine_if(!is_d3d12)
     ok(!wait_result, "Got unexpected wait result %#lx.\n", wait_result);
     wait_result = WaitForSingleObject(semaphore, 0);
-    todo_wine_if(!is_d3d12)
     ok(!wait_result, "Got unexpected wait result %#lx.\n", wait_result);
     wait_result = WaitForSingleObject(semaphore, 100);
-    todo_wine_if(!is_d3d12)
     ok(wait_result == WAIT_TIMEOUT, "Got unexpected wait result %#lx.\n", wait_result);
 
     /* lowering the maximum frame latency doesn't seem to impact the
      * semaphore */
     hr = IDXGISwapChain2_SetMaximumFrameLatency(swapchain2, 1);
-    todo_wine_if(!is_d3d12)
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
     hr = IDXGISwapChain2_GetMaximumFrameLatency(swapchain2, &frame_latency);
-    todo_wine_if(!is_d3d12)
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
-    todo_wine_if(!is_d3d12)
     ok(frame_latency == 1, "Got unexpected frame latency %#x.\n", frame_latency);
 
     wait_result = WaitForSingleObject(semaphore, 100);
-    todo_wine_if(!is_d3d12)
     ok(wait_result == WAIT_TIMEOUT, "Got unexpected wait result %#lx.\n", wait_result);
 
     for (i = 0; i < 5; i++)
@@ -7433,12 +7555,10 @@ static void test_frame_latency_event(IUnknown *device, BOOL is_d3d12)
         ok(hr == S_OK, "Present %u failed with hr %#lx.\n", i, hr);
 
         wait_result = WaitForSingleObject(semaphore, 100);
-        todo_wine_if(!is_d3d12)
         ok(!wait_result, "Got unexpected wait result %#lx.\n", wait_result);
     }
 
     wait_result = WaitForSingleObject(semaphore, 100);
-    todo_wine_if(!is_d3d12)
     ok(wait_result == WAIT_TIMEOUT, "Got unexpected wait result %#lx.\n", wait_result);
 
     /* each frame presentation releases the semaphore */
@@ -7453,12 +7573,10 @@ static void test_frame_latency_event(IUnknown *device, BOOL is_d3d12)
     for (i = 0; i < 5; i++)
     {
         wait_result = WaitForSingleObject(semaphore, 100);
-        todo_wine_if(!is_d3d12)
         ok(!wait_result, "Got unexpected wait result %#lx.\n", wait_result);
     }
 
     wait_result = WaitForSingleObject(semaphore, 100);
-    todo_wine_if(!is_d3d12)
     ok(wait_result == WAIT_TIMEOUT, "Got unexpected wait result %#lx.\n", wait_result);
 
     if (is_d3d12)
@@ -8623,12 +8741,12 @@ START_TEST(dxgi)
     run_queued_tests();
 
     /* These tests use full-screen swapchains, so shouldn't run in parallel. */
-    test_create_swapchain();
     test_inexact_modes();
     test_gamma_control();
     test_multi_adapter();
     test_swapchain_parameters();
     test_swapchain_window_styles();
+    run_on_d3d10(test_create_swapchain);
     run_on_d3d10(test_set_fullscreen);
     run_on_d3d10(test_resize_target);
     run_on_d3d10(test_resize_fullscreen);
@@ -8663,6 +8781,7 @@ START_TEST(dxgi)
         ID3D12Debug_Release(debug);
     }
 
+    run_on_d3d12(test_create_swapchain);
     run_on_d3d12(test_set_fullscreen);
     run_on_d3d12(test_resize_target);
     run_on_d3d12(test_resize_fullscreen);
diff --git a/dlls/dxgi/utils.c b/dlls/dxgi/utils.c
index 35fcc076ccb4..3e07f27ff0ab 100644
--- a/dlls/dxgi/utils.c
+++ b/dlls/dxgi/utils.c
@@ -562,6 +562,12 @@ static unsigned int wined3d_swapchain_flags_from_dxgi(unsigned int flags)
         wined3d_flags |= WINED3D_SWAPCHAIN_GDI_COMPATIBLE;
     }
 
+    if (flags & DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT)
+    {
+        flags &= ~DXGI_SWAP_CHAIN_FLAG_FRAME_LATENCY_WAITABLE_OBJECT;
+        wined3d_flags |= WINED3D_SWAPCHAIN_FRAME_LATENCY_WAITABLE_OBJECT;
+    }
+
     if (flags)
         FIXME("Unhandled flags %#x.\n", flags);
 
diff --git a/dlls/fltmgr.sys/fltmgr.sys.spec b/dlls/fltmgr.sys/fltmgr.sys.spec
index 8943b9f85cf3..f123a49dbb06 100644
--- a/dlls/fltmgr.sys/fltmgr.sys.spec
+++ b/dlls/fltmgr.sys/fltmgr.sys.spec
@@ -3,7 +3,7 @@
 @ stub FltAcquireResourceExclusive
 @ stub FltAcquireResourceShared
 @ stub FltAllocateCallbackData
-@ stub FltAllocateContext
+@ stdcall FltAllocateContext(ptr long long long ptr)
 @ stub FltAllocateDeferredIoWorkItem
 @ stub FltAllocateFileLock
 @ stub FltAllocateGenericWorkItem
@@ -27,11 +27,11 @@
 @ stub FltClearCancelCompletion
 @ stub FltClose
 @ stub FltCloseClientPort
-@ stub FltCloseCommunicationPort
+@ stdcall FltCloseCommunicationPort(ptr)
 @ stub FltCompareInstanceAltitudes
 @ stub FltCompletePendedPostOperation
 @ stub FltCompletePendedPreOperation
-@ stub FltCreateCommunicationPort
+@ stdcall FltCreateCommunicationPort(ptr ptr ptr ptr ptr ptr ptr long)
 @ stub FltCreateFile
 @ stub FltCreateFileEx
 @ stub FltCreateSystemVolumeInformationFolder
@@ -53,7 +53,7 @@
 @ stub FltEnumerateInstanceInformationByVolume
 @ stub FltEnumerateInstances
 @ stub FltEnumerateVolumeInformation
-@ stub FltEnumerateVolumes
+@ stdcall FltEnumerateVolumes(ptr ptr long ptr)
 @ stub FltFlushBuffers
 @ stub FltFreeCallbackData
 @ stub FltFreeDeferredIoWorkItem
@@ -68,8 +68,8 @@
 @ stub FltGetDeviceObject
 @ stub FltGetDiskDeviceObject
 @ stub FltGetFileContext
-@ stub FltGetFileNameInformation
-@ stub FltGetFileNameInformationUnsafe
+@ stdcall FltGetFileNameInformation(ptr long ptr)
+@ stdcall FltGetFileNameInformationUnsafe(ptr ptr long ptr)
 @ stub FltGetFilterFromInstance
 @ stub FltGetFilterFromName
 @ stub FltGetFilterInformation
@@ -80,7 +80,7 @@
 @ stub FltGetRequestorProcess
 @ stub FltGetRequestorProcessId
 @ stdcall FltGetRoutineAddress(str)
-@ stub FltGetStreamContext
+@ stdcall FltGetStreamContext(ptr ptr ptr)
 @ stub FltGetStreamHandleContext
 @ stub FltGetSwappedBufferMdlAddress
 @ stub FltGetTopInstance
@@ -113,26 +113,26 @@
 @ stub FltOplockFsctrl
 @ stub FltOplockIsFastIoPossible
 @ stub FltParseFileName
-@ stub FltParseFileNameInformation
+@ stdcall FltParseFileNameInformation(ptr)
 @ stub FltPerformAsynchronousIo
 @ stub FltPerformSynchronousIo
 @ stub FltProcessFileLock
 @ stub FltPurgeFileNameInformationCache
 @ stub FltQueryEaFile
-@ stub FltQueryInformationFile
+@ stdcall FltQueryInformationFile(ptr ptr long ptr ptr ptr)
 @ stub FltQuerySecurityObject
 @ stub FltQueryVolumeInformation
 @ stub FltQueryVolumeInformationFile
 @ stub FltQueueDeferredIoWorkItem
 @ stub FltQueueGenericWorkItem
-@ stub FltReadFile
+@ stdcall FltReadFile(ptr ptr ptr long ptr long ptr ptr ptr)
 @ stub FltReferenceContext
 @ stub FltReferenceFileNameInformation
 @ stdcall FltRegisterFilter(ptr ptr ptr)
 @ stub FltReissueSynchronousIo
-@ stub FltReleaseContext
+@ stdcall FltReleaseContext(ptr)
 @ stub FltReleaseContexts
-@ stub FltReleaseFileNameInformation
+@ stdcall FltReleaseFileNameInformation(ptr)
 @ stdcall FltReleasePushLock(ptr)
 @ stub FltReleaseResource
 @ stub FltRequestOperationStatusCallback
@@ -146,7 +146,7 @@
 @ stub FltSetInformationFile
 @ stub FltSetInstanceContext
 @ stub FltSetSecurityObject
-@ stub FltSetStreamContext
+@ stdcall FltSetStreamContext(ptr ptr long ptr ptr)
 @ stub FltSetStreamHandleContext
 @ stub FltSetVolumeContext
 @ stub FltSetVolumeInformation
diff --git a/dlls/fltmgr.sys/main.c b/dlls/fltmgr.sys/main.c
index 54ee2ce1e127..60fc46db9cb2 100644
--- a/dlls/fltmgr.sys/main.c
+++ b/dlls/fltmgr.sys/main.c
@@ -149,3 +149,89 @@ void WINAPI FltFreeSecurityDescriptor(PSECURITY_DESCRIPTOR descriptor)
 {
     ExFreePool(descriptor);
 }
+
+NTSTATUS WINAPI FltAllocateContext(PFLT_FILTER filter, FLT_CONTEXT_TYPE context, SIZE_T size, POOL_TYPE type, PFLT_CONTEXT *out)
+{
+    FIXME("%p, %d, %Iu, %u, %p\n", filter, context, size, type, *out);
+    return STATUS_NOT_SUPPORTED;
+}
+
+void WINAPI FltReleaseContext(PFLT_CONTEXT context)
+{
+    FIXME("%p\n", context);
+}
+
+NTSTATUS WINAPI FltGetStreamContext(PFLT_INSTANCE instance, PFILE_OBJECT  fileobject, PFLT_CONTEXT  *context)
+{
+    FIXME("%p, %p, %p\n", instance, fileobject, context);
+    return STATUS_NOT_SUPPORTED;
+}
+
+NTSTATUS WINAPI FltSetStreamContext(PFLT_INSTANCE instance, PFILE_OBJECT fileobject, FLT_SET_CONTEXT_OPERATION operation,
+        PFLT_CONTEXT new_context, PFLT_CONTEXT *old_context)
+{
+    FIXME("%p, %p, %d, %p, %p\n", instance, fileobject, operation, new_context, old_context);
+    return STATUS_NOT_SUPPORTED;
+}
+
+NTSTATUS WINAPI FltQueryInformationFile(PFLT_INSTANCE instance, PFILE_OBJECT fileobject, PVOID information,
+        ULONG length, FILE_INFORMATION_CLASS file_information_class, PULONG ret_len)
+{
+    FIXME("%p, %p, %p, %lu, %d, %p\n", instance, fileobject, information, length, file_information_class, ret_len);
+    return STATUS_NOT_SUPPORTED;
+}
+
+NTSTATUS WINAPI FltGetFileNameInformationUnsafe(PFILE_OBJECT fileobject, PFLT_INSTANCE instance,
+        FLT_FILE_NAME_OPTIONS options, PFLT_FILE_NAME_INFORMATION *FileNameInformation)
+{
+    FIXME("%p, %p, %lu, %p\n", fileobject, instance, options, FileNameInformation);
+    return STATUS_NOT_SUPPORTED;
+}
+
+NTSTATUS WINAPI FltReadFile(PFLT_INSTANCE instance, PFILE_OBJECT fileobject, PLARGE_INTEGER offset,
+        ULONG length, PVOID buffer, FLT_IO_OPERATION_FLAGS flags, PULONG read,
+        PFLT_COMPLETED_ASYNC_IO_CALLBACK callback, PVOID context)
+{
+    FIXME("%p, %p, %p, %lu, %p, 0x%lx, %p, %p, %p\n", instance, fileobject, offset, length, buffer, flags, read, callback, context);
+    return STATUS_NOT_SUPPORTED;
+}
+
+NTSTATUS WINAPI FltParseFileNameInformation(PFLT_FILE_NAME_INFORMATION information)
+{
+    FIXME("%p\n", information);
+    return STATUS_NOT_SUPPORTED;
+}
+
+void WINAPI FltReleaseFileNameInformation(PFLT_FILE_NAME_INFORMATION information)
+{
+    FIXME("%p\n", information);
+}
+
+NTSTATUS WINAPI FltGetFileNameInformation(PFLT_CALLBACK_DATA callback, FLT_FILE_NAME_OPTIONS options, PFLT_FILE_NAME_INFORMATION *information)
+{
+    FIXME("%p, %lu, %p\n", callback, options, information);
+    return STATUS_NOT_SUPPORTED;
+}
+
+NTSTATUS WINAPI FltCreateCommunicationPort(PFLT_FILTER filter, PFLT_PORT *port, POBJECT_ATTRIBUTES attributes,
+        PVOID cookie, PFLT_CONNECT_NOTIFY  connect_callback, PFLT_DISCONNECT_NOTIFY disconnect_callback,
+        PFLT_MESSAGE_NOTIFY message_callback, LONG max_connections)
+{
+    FIXME("%p, %p, %p, %p, %p, %p, %p, %ld\n", filter, port, attributes, cookie, connect_callback, disconnect_callback,
+                        message_callback, max_connections);
+    return STATUS_NOT_SUPPORTED;
+}
+
+void WINAPI FltCloseCommunicationPort (PFLT_PORT ServerPort)
+{
+    FIXME("%p\n", ServerPort);
+}
+
+NTSTATUS WINAPI FltEnumerateVolumes (PFLT_FILTER filter, PFLT_VOLUME *volumes, ULONG size, PULONG returned)
+{
+    FIXME("%p, %p, %lu, %p\n", filter, volumes, size, returned);
+
+    if (returned)
+        *returned = 0;
+    return STATUS_NOT_SUPPORTED;
+}
diff --git a/dlls/gdiplus/image.c b/dlls/gdiplus/image.c
index 4d537fd4f961..6f6baa304f34 100644
--- a/dlls/gdiplus/image.c
+++ b/dlls/gdiplus/image.c
@@ -4492,7 +4492,7 @@ static GpStatus get_decoder_info(IStream* stream, const struct image_codec **res
     /* FIXME: This assumes all codecs have signatures <= 8 bytes in length */
     hr = IStream_Read(stream, signature, 8, &bytesread);
     if (FAILED(hr)) return hresult_to_status(hr);
-    if (hr == S_FALSE || bytesread == 0) return GenericError;
+    if (hr == S_FALSE || bytesread == 0) return InvalidParameter;
 
     for (i = 0; i < NUM_CODECS; i++) {
         if ((codecs[i].info.Flags & ImageCodecFlagsDecoder) &&
diff --git a/dlls/gdiplus/tests/image.c b/dlls/gdiplus/tests/image.c
index 7548ed9e725b..79043253b389 100644
--- a/dlls/gdiplus/tests/image.c
+++ b/dlls/gdiplus/tests/image.c
@@ -6778,6 +6778,7 @@ static void test_png_histogram_property(void)
 
 static void test_GdipLoadImageFromStream(void)
 {
+    ULARGE_INTEGER li = { 0 };
     IStream *stream;
     GpStatus status;
     GpImage *image;
@@ -6793,6 +6794,17 @@ static void test_GdipLoadImageFromStream(void)
     ok(status == InvalidParameter, "Unexpected return value %d.\n", status);
     ok(image == (void *)0xdeadbeef, "Unexpected image pointer.\n");
 
+    hglob = GlobalAlloc(0, 0);
+    hr = CreateStreamOnHGlobal(hglob, TRUE, &stream);
+    ok(hr == S_OK, "Failed to create a stream.\n");
+    hr = IStream_SetSize(stream, li);
+    ok(hr == S_OK, "SetSize failed\n");
+
+    status = GdipLoadImageFromStream(stream, &image);
+    ok(status == InvalidParameter, "Unexpected return value %d.\n", status);
+
+    IStream_Release(stream);
+
     hglob = GlobalAlloc(0, sizeof(pngimage));
     data = GlobalLock (hglob);
     memcpy(data, pngimage, sizeof(pngimage));
diff --git a/dlls/hhctrl.ocx/help.c b/dlls/hhctrl.ocx/help.c
index 0c3562c765e5..3a029685af6b 100644
--- a/dlls/hhctrl.ocx/help.c
+++ b/dlls/hhctrl.ocx/help.c
@@ -1608,7 +1608,7 @@ static BOOL AddIndexPopup(HHInfo *info)
     hwndPopup = CreateWindowExW(WS_EX_LEFT | WS_EX_LTRREADING | WS_EX_APPWINDOW
                                  | WS_EX_WINDOWEDGE | WS_EX_RIGHTSCROLLBAR,
                                 L"HH Popup", window_title, WS_POPUPWINDOW
-                                 | WS_OVERLAPPEDWINDOW | WS_VISIBLE
+                                 | WS_OVERLAPPEDWINDOW
                                  | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, CW_USEDEFAULT,
                                 CW_USEDEFAULT, 300, 200, info->WinType.hwndHelp,
                                 NULL, hhctrl_hinstance, NULL);
@@ -1623,7 +1623,6 @@ static BOOL AddIndexPopup(HHInfo *info)
     if (!hwndCallback)
         return FALSE;
 
-    ShowWindow(hwndPopup, SW_HIDE);
     hwndList = CreateWindowExW(WS_EX_CLIENTEDGE, WC_LISTVIEWW, szEmpty,
                                WS_CHILD | WS_BORDER | LVS_SINGLESEL | LVS_REPORT
                                 | LVS_NOCOLUMNHEADER, 50, 50, 100, 100,
diff --git a/dlls/hidclass.sys/device.c b/dlls/hidclass.sys/device.c
index 5b7a675db7f2..4a28ec01344e 100644
--- a/dlls/hidclass.sys/device.c
+++ b/dlls/hidclass.sys/device.c
@@ -416,7 +416,7 @@ static const WCHAR *find_device_string( const WCHAR *device_id, ULONG index )
 struct completion_params
 {
     HID_XFER_PACKET packet;
-    ULONG padding;
+    ULONG report_len;
     IRP *irp;
 };
 
@@ -428,7 +428,7 @@ static NTSTATUS CALLBACK xfer_completion( DEVICE_OBJECT *device, IRP *irp, void
     TRACE( "device %p, irp %p, context %p\n", device, irp, context );
 
     orig_irp->IoStatus = irp->IoStatus;
-    orig_irp->IoStatus.Information -= params->padding;
+    if (params->report_len) orig_irp->IoStatus.Information = params->report_len;
     IoCompleteRequest( orig_irp, IO_NO_INCREMENT );
 
     free( params );
@@ -498,7 +498,8 @@ static NTSTATUS hid_device_xfer_report( struct phys_device *pdo, ULONG code, IRP
                                              sizeof(params->packet), TRUE, NULL, NULL );
         break;
     case IOCTL_HID_WRITE_REPORT:
-        params->padding = 1 - offset;
+        /* WriteFile returns the output report length, not the minidriver count */
+        params->report_len = report_len;
         /* fallthrough */
     case IOCTL_HID_SET_FEATURE:
     case IOCTL_HID_SET_OUTPUT_REPORT:
diff --git a/dlls/http.sys/http.c b/dlls/http.sys/http.c
index 260e022f20a2..aee6aea62257 100644
--- a/dlls/http.sys/http.c
+++ b/dlls/http.sys/http.c
@@ -684,7 +684,7 @@ static DWORD WINAPI request_thread_proc(void *arg)
 
     TRACE("Starting request thread.\n");
 
-    while (!WaitForSingleObject(request_event, INFINITE))
+    while (!thread_stop && !WaitForSingleObject(request_event, INFINITE) && !thread_stop)
     {
         EnterCriticalSection(&http_cs);
 
diff --git a/dlls/inkobj/Makefile.in b/dlls/inkobj/Makefile.in
index bc9f90577a5b..0fe13a27a14a 100644
--- a/dlls/inkobj/Makefile.in
+++ b/dlls/inkobj/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = inkobj.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	inkobj.c
diff --git a/dlls/kernel32/kernel32.spec b/dlls/kernel32/kernel32.spec
index 87eb83a225d1..57b1b9f335a0 100644
--- a/dlls/kernel32/kernel32.spec
+++ b/dlls/kernel32/kernel32.spec
@@ -505,7 +505,7 @@
 @ stdcall -import FindFirstVolumeW(ptr long)
 @ stdcall -import FindNextChangeNotification(long)
 @ stdcall -import FindNextFileA(long ptr)
-# @ stub FindNextFileNameW
+@ stdcall -import FindNextFileNameW(ptr ptr ptr)
 @ stdcall -import FindNextFileW(long ptr)
 @ stdcall -import FindNextStreamW(long ptr)
 @ stdcall FindNextVolumeA(long ptr long)
@@ -1190,6 +1190,9 @@
 @ stdcall Process32Next (ptr ptr)
 @ stdcall Process32NextW (ptr ptr)
 @ stdcall -import ProcessIdToSessionId(long ptr)
+@ stdcall -import PssCaptureSnapshot(ptr long long ptr)
+@ stdcall -import PssFreeSnapshot(ptr ptr)
+@ stdcall -import PssQuerySnapshot(ptr long ptr long)
 @ stdcall -import PulseEvent(long)
 @ stdcall -import PurgeComm(long long)
 @ stdcall -i386 -private -norelay QT_Thunk() krnl386.exe16.QT_Thunk
diff --git a/dlls/kernel32/tests/file.c b/dlls/kernel32/tests/file.c
index 8dac53384bb2..2427129d093b 100644
--- a/dlls/kernel32/tests/file.c
+++ b/dlls/kernel32/tests/file.c
@@ -5759,6 +5759,83 @@ static void test_GetFinalPathNameByHandleW(void)
     ok(lstrcmpiW(test_path, result_path) == 0, "Expected %s, got %s\n",
        wine_dbgstr_w(test_path), wine_dbgstr_w(result_path));
     CloseHandle(file);
+
+    wcscpy(test_path, temp_path);
+    wcscat(test_path, L"Test.Dat");
+    DeleteFileW(test_path);
+    file = CreateFileW(test_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileW error %lu\n", GetLastError());
+    count = pGetFinalPathNameByHandleW(file, result_path, ARRAY_SIZE(result_path), 0);
+    ok(count == lstrlenW(test_path) + 4, "Expected length %u, got %lu\n", lstrlenW(test_path), count);
+    ok(wcscmp(test_path, result_path + 4) == 0, "Expected %s, got %s\n",
+       wine_dbgstr_w(test_path), wine_dbgstr_w(result_path));
+    CloseHandle(file);
+
+    wcscpy(result_path, temp_path);
+    wcscat(result_path, L"test.dat");
+    file = CreateFileW(result_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileW error %lu\n", GetLastError());
+    count = pGetFinalPathNameByHandleW(file, result_path, ARRAY_SIZE(result_path), 0);
+    ok(count == lstrlenW(test_path) + 4, "Expected length %u, got %lu\n", lstrlenW(test_path), count);
+    todo_wine ok(wcscmp(test_path, result_path + 4) == 0, "Expected %s, got %s\n",
+       wine_dbgstr_w(test_path), wine_dbgstr_w(result_path));
+    CloseHandle(file);
+
+    wcscpy(result_path, temp_path);
+    wcscat(result_path, L"test.dat");
+    file = CreateFileW(result_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, 0, 0);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileW error %lu\n", GetLastError());
+    count = pGetFinalPathNameByHandleW(file, result_path, ARRAY_SIZE(result_path), 0);
+    ok(count == lstrlenW(test_path) + 4, "Expected length %u, got %lu\n", lstrlenW(test_path), count);
+    todo_wine ok(wcscmp(test_path, result_path + 4) == 0, "Expected %s, got %s\n",
+       wine_dbgstr_w(test_path), wine_dbgstr_w(result_path));
+    wcscpy(test_path, temp_path);
+    wcscat(test_path, L"test_renamed.dat");
+    success = MoveFileW(result_path + 4, test_path);
+    ok(success, "got error %ld.\n", GetLastError());
+
+    count = pGetFinalPathNameByHandleW(file, result_path, ARRAY_SIZE(result_path), 0);
+    todo_wine ok(count == lstrlenW(test_path) + 4, "Expected length %u, got %lu\n", lstrlenW(test_path), count);
+    todo_wine ok(wcscmp(test_path, result_path + 4) == 0, "Expected %s, got %s\n",
+       wine_dbgstr_w(test_path), wine_dbgstr_w(result_path));
+    CloseHandle(file);
+
+    success = DeleteFileW(test_path);
+    ok(success, "got error %ld.\n", GetLastError());
+
+    wcscpy(test_path, temp_path);
+    wcscat(test_path, L"link");
+    success = CreateSymbolicLinkW(test_path, temp_path, SYMBOLIC_LINK_FLAG_DIRECTORY | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE);
+    ok(success, "got error %ld.\n", GetLastError());
+
+    wcscpy(test_path, temp_path);
+    wcscat(test_path, L"LINK");
+    file = CreateFileW(test_path, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+                       NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileW error %lu\n", GetLastError());
+    wcscpy(test_path, temp_path);
+    wcscat(test_path, L"link");
+    count = pGetFinalPathNameByHandleW(file, result_path, ARRAY_SIZE(result_path), 0);
+    ok(count == lstrlenW(test_path) + 4, "Expected length %u, got %lu\n", lstrlenW(test_path), count);
+    todo_wine ok(wcscmp(test_path, result_path + 4) == 0, "Expected %s, got %s\n",
+       wine_dbgstr_w(test_path), wine_dbgstr_w(result_path));
+    CloseHandle(file);
+
+    wcscat(test_path, L"\\Test.Dat");
+    file = CreateFileW(test_path, GENERIC_READ | GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, 0);
+    ok(file != INVALID_HANDLE_VALUE, "CreateFileW error %lu\n", GetLastError());
+    count = pGetFinalPathNameByHandleW(file, result_path, ARRAY_SIZE(result_path), 0);
+    wcscpy(test_path, temp_path);
+    wcscat(test_path, L"Test.Dat");
+    ok(count == lstrlenW(test_path) + 4, "Expected length %u, got %lu\n", lstrlenW(test_path), count);
+    ok(wcscmp(test_path, result_path + 4) == 0, "Expected %s, got %s\n",
+       wine_dbgstr_w(test_path), wine_dbgstr_w(result_path));
+    CloseHandle(file);
+
+    wcscpy(test_path, temp_path);
+    wcscat(test_path, L"LINK");
+    success = RemoveDirectoryW(test_path);
+    ok(success, "got error %ld.\n", GetLastError());
 }
 
 static void test_SetFileInformationByHandle(void)
diff --git a/dlls/kernelbase/file.c b/dlls/kernelbase/file.c
index 9b66cecee6ed..113aa8e171a2 100644
--- a/dlls/kernelbase/file.c
+++ b/dlls/kernelbase/file.c
@@ -1485,6 +1485,18 @@ HANDLE WINAPI FindFirstFileNameW( const WCHAR *file_name, DWORD flags, DWORD *le
     return INVALID_HANDLE_VALUE;
 }
 
+
+/******************************************************************************
+ *     FindNextFileNameW   (kernelbase.@)
+ */
+BOOL WINAPI FindNextFileNameW( HANDLE handle, DWORD *len, WCHAR *link_name )
+{
+    FIXME( "(%p, %p, %p): stub.\n", handle, len, link_name );
+    SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
+    return FALSE;
+}
+
+
 /**************************************************************************
  *	FindFirstStreamW   (kernelbase.@)
  */
diff --git a/dlls/kernelbase/kernelbase.spec b/dlls/kernelbase/kernelbase.spec
index 6c1d14e7dca6..0dcf3ee99d37 100644
--- a/dlls/kernelbase/kernelbase.spec
+++ b/dlls/kernelbase/kernelbase.spec
@@ -376,7 +376,7 @@
 @ stdcall FindNLSStringEx(wstr long wstr long wstr long ptr ptr ptr long)
 @ stdcall FindNextChangeNotification(long)
 @ stdcall FindNextFileA(long ptr)
-# @ stub FindNextFileNameW
+@ stdcall FindNextFileNameW(ptr ptr ptr)
 @ stdcall FindNextFileW(long ptr)
 @ stdcall FindNextStreamW(long ptr)
 @ stdcall FindNextVolumeW(long ptr long)
@@ -1221,10 +1221,10 @@
 # @ stub PsmIsChildKey
 # @ stub PsmIsDynamicKey
 # @ stub PsmIsValidKey
-# @ stub PssCaptureSnapshot
+@ stdcall PssCaptureSnapshot(ptr long long ptr)
 # @ stub PssDuplicateSnapshot
-# @ stub PssFreeSnapshot
-# @ stub PssQuerySnapshot
+@ stdcall PssFreeSnapshot(ptr ptr)
+@ stdcall PssQuerySnapshot(ptr long ptr long)
 # @ stub PssWalkMarkerCreate
 # @ stub PssWalkMarkerFree
 # @ stub PssWalkMarkerGetPosition
diff --git a/dlls/kernelbase/process.c b/dlls/kernelbase/process.c
index d16035fd68ba..25b3f4fffb9b 100644
--- a/dlls/kernelbase/process.c
+++ b/dlls/kernelbase/process.c
@@ -27,6 +27,7 @@
 #include "winnls.h"
 #include "wincontypes.h"
 #include "winternl.h"
+#include "processsnapshot.h"
 
 #include "kernelbase.h"
 #include "wine/debug.h"
@@ -1907,3 +1908,30 @@ BOOL WINAPI DECLSPEC_HOTPATCH CompareObjectHandles( HANDLE first, HANDLE second
 {
     return set_ntstatus( NtCompareObjects( first, second ));
 }
+
+/***********************************************************************
+ *           PssQuerySnapshot   (kernelbase.@)
+ */
+DWORD WINAPI PssQuerySnapshot( HPSS handle, PSS_QUERY_INFORMATION_CLASS class, void *buffer, DWORD len )
+{
+    FIXME( "(%p %u %p %lu)\n", handle ,class, buffer, len );
+    return ERROR_NOT_FOUND;
+}
+
+/***********************************************************************
+ *           PssFreeSnapshot   (kernelbase.@)
+ */
+DWORD WINAPI PssFreeSnapshot( HANDLE hprocess, HPSS handle )
+{
+    FIXME( "(%p %p)\n", hprocess , handle );
+    return ERROR_SUCCESS;
+}
+
+/***********************************************************************
+ *           PssCaptureSnapshot   (kernelbase.@)
+ */
+DWORD WINAPI PssCaptureSnapshot( HANDLE hprocess, PSS_CAPTURE_FLAGS flags, DWORD ctx_flags, HPSS *handle )
+{
+    FIXME( "(%p %u %lu %p)\n", hprocess , flags, ctx_flags, handle );
+    return ERROR_NOT_FOUND;
+}
diff --git a/dlls/magnification/Makefile.in b/dlls/magnification/Makefile.in
index c557e688b4b8..78adf6463383 100644
--- a/dlls/magnification/Makefile.in
+++ b/dlls/magnification/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = magnification.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	main.c
diff --git a/dlls/mfsrcsnk/media_source.c b/dlls/mfsrcsnk/media_source.c
index 28b0d34c46b6..3c66fc76d4a4 100644
--- a/dlls/mfsrcsnk/media_source.c
+++ b/dlls/mfsrcsnk/media_source.c
@@ -1825,7 +1825,7 @@ static WCHAR *get_byte_stream_url(IMFByteStream *stream, const WCHAR *url)
         if (FAILED(hr = IMFAttributes_GetString(attributes, &MF_BYTESTREAM_ORIGIN_NAME,
                 buffer, ARRAY_SIZE(buffer), &size)))
             WARN("Failed to get MF_BYTESTREAM_ORIGIN_NAME got size %#x, hr %#lx\n", size, hr);
-        else
+        else if (*buffer) /* an empty origin name does not override the url */
             url = buffer;
         IMFAttributes_Release(attributes);
     }
diff --git a/dlls/mmdevapi/spatialaudio.c b/dlls/mmdevapi/spatialaudio.c
index 55f0c2d6772d..864b33136e66 100644
--- a/dlls/mmdevapi/spatialaudio.c
+++ b/dlls/mmdevapi/spatialaudio.c
@@ -379,8 +379,14 @@ static HRESULT WINAPI SAORS_Stop(ISpatialAudioObjectRenderStream *iface)
 static HRESULT WINAPI SAORS_Reset(ISpatialAudioObjectRenderStream *iface)
 {
     SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
-    FIXME("(%p)->()\n", This);
-    return E_NOTIMPL;
+    HRESULT hr;
+
+    TRACE("(%p)->()\n", This);
+
+    hr = IAudioClient_Reset(This->client);
+    if (hr == AUDCLNT_E_NOT_STOPPED)
+        return SPTLAUDCLNT_E_STREAM_NOT_STOPPED;
+    return hr;
 }
 
 static HRESULT WINAPI SAORS_BeginUpdatingAudioObjects(ISpatialAudioObjectRenderStream *iface,
diff --git a/dlls/mmdevapi/tests/spatialaudio.c b/dlls/mmdevapi/tests/spatialaudio.c
index 2a7fd0410601..4055f860fb41 100644
--- a/dlls/mmdevapi/tests/spatialaudio.c
+++ b/dlls/mmdevapi/tests/spatialaudio.c
@@ -309,6 +309,9 @@ static void test_audio_object_buffers(void)
     hr = ISpatialAudioClient_ActivateSpatialAudioStream(sac, &activation_params_prop, &IID_ISpatialAudioObjectRenderStream, (void**)&sas);
     ok(hr == S_OK, "Failed to activate spatial audio stream: 0x%08lx\n", hr);
 
+    hr = ISpatialAudioObjectRenderStream_Reset(sas);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+
     hr = ISpatialAudioClient_GetMaxFrameCount(sac, &format, &max_frame_count);
     ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
     frame_count = format.nSamplesPerSec / 100; /* 10ms */
@@ -459,6 +462,27 @@ static void test_audio_object_buffers(void)
     hr = ISpatialAudioObjectRenderStream_EndUpdatingAudioObjects(sas);
     ok(hr == S_OK, "Failed to end updating audio objects: 0x%08lx\n", hr);
 
+    hr = ISpatialAudioObjectRenderStream_Reset(sas);
+    ok(hr == SPTLAUDCLNT_E_STREAM_NOT_STOPPED, "got %#lx.\n", hr);
+
+    hr = ISpatialAudioObjectRenderStream_Stop(sas);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+
+    hr = ISpatialAudioObjectRenderStream_Reset(sas);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+
+    hr = ISpatialAudioObjectRenderStream_BeginUpdatingAudioObjects(sas, &dyn_object_count, &frame_count);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+
+    hr = ISpatialAudioObjectRenderStream_Reset(sas);
+    todo_wine ok(hr == S_OK, "got %#lx.\n", hr);
+
+    hr = ISpatialAudioObjectRenderStream_EndUpdatingAudioObjects(sas);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+
+    hr = ISpatialAudioObjectRenderStream_Reset(sas);
+    ok(hr == S_OK, "got %#lx.\n", hr);
+
     for (i = 0; i < ARRAYSIZE(sao); i++)
     {
         ISpatialAudioObject_Release(sao[i]);
diff --git a/dlls/mscat32/Makefile.in b/dlls/mscat32/Makefile.in
index cc53ef65cc14..837c27f571f0 100644
--- a/dlls/mscat32/Makefile.in
+++ b/dlls/mscat32/Makefile.in
@@ -1,4 +1,2 @@
 MODULE    = mscat32.dll
 IMPORTS   = wintrust
-
-EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/mshtml/htmlelem.c b/dlls/mshtml/htmlelem.c
index b90fc24d284c..ab643a74d89a 100644
--- a/dlls/mshtml/htmlelem.c
+++ b/dlls/mshtml/htmlelem.c
@@ -1214,6 +1214,9 @@ static HRESULT WINAPI HTMLElement_removeAttribute(IHTMLElement *iface, BSTR strA
             list_remove(&attr->entry);
             IHTMLDOMNode_Release(&attr->elem->node.IHTMLDOMNode_iface);
             attr->elem = NULL;
+
+            /* The collection also keeps ref to us, release it */
+            IHTMLDOMAttribute_Release(&attr->IHTMLDOMAttribute_iface);
         }
 
         if(id == DISPID_IHTMLELEMENT_STYLE) {
@@ -8206,6 +8209,7 @@ static HRESULT HTMLAttributeCollection_get_dispid(DispatchEx *dispex, const WCHA
             if(iter == nsattr)
                 break;
         }
+        nsIDOMAttr_Release(nsattr);
 
         assert(i < length);
         *dispid = MSHTML_DISPID_CUSTOM_MIN + i;
diff --git a/dlls/mshtml/htmlframe.c b/dlls/mshtml/htmlframe.c
index e2a8b1f96a58..5b8d542d6127 100644
--- a/dlls/mshtml/htmlframe.c
+++ b/dlls/mshtml/htmlframe.c
@@ -850,7 +850,7 @@ static void HTMLFrameElement_traverse(DispatchEx *dispex, nsCycleCollectionTrave
 static void HTMLFrameElement_unlink(DispatchEx *dispex)
 {
     HTMLFrameElement *This = frame_from_DispatchEx(dispex);
-    HTMLDOMNode_unlink(dispex);
+    HTMLElement_unlink(dispex);
     unlink_ref(&This->framebase.nsframe);
 }
 
@@ -1297,7 +1297,7 @@ static void HTMLIFrame_traverse(DispatchEx *dispex, nsCycleCollectionTraversalCa
 static void HTMLIFrame_unlink(DispatchEx *dispex)
 {
     HTMLIFrame *This = iframe_from_DispatchEx(dispex);
-    HTMLDOMNode_unlink(dispex);
+    HTMLElement_unlink(dispex);
     unlink_ref(&This->framebase.nsiframe);
 }
 
diff --git a/dlls/mshtml/htmlnode.c b/dlls/mshtml/htmlnode.c
index 022acccf9159..b82ab5710de0 100644
--- a/dlls/mshtml/htmlnode.c
+++ b/dlls/mshtml/htmlnode.c
@@ -1294,6 +1294,7 @@ static HRESULT create_node(HTMLDocumentNode *doc, nsIDOMNode *nsnode, HTMLDOMNod
         nsres = nsIDOMNode_QueryInterface(nsnode, &IID_nsIDOMAttr, (void **)&nsattr);
         assert(nsres == NS_OK);
         hres = create_attr_node(doc, nsattr, &attr);
+        nsIDOMAttr_Release(nsattr);
         if(SUCCEEDED(hres))
             *ret = &attr->node;
         return hres;
diff --git a/dlls/mshtml/tests/dom.c b/dlls/mshtml/tests/dom.c
index b3f49171a942..bfaad84c1868 100644
--- a/dlls/mshtml/tests/dom.c
+++ b/dlls/mshtml/tests/dom.c
@@ -11066,6 +11066,7 @@ static void test_attr(IHTMLDocument2 *doc, IHTMLElement *elem)
     test_attr_specified(attr, VARIANT_FALSE);
     test_attr_expando(attr, VARIANT_FALSE);
     test_attr_node(attr, doc);
+    IHTMLDOMAttribute_Release(attr);
 
     for(i = 0; i < ARRAY_SIZE(elem_attr_props); i++) {
         BOOL has_attr = elem_has_attr((IUnknown*)elem, elem_attr_props[i]);
@@ -13591,6 +13592,7 @@ static void test_attribute_node_across_modes(void)
     IDispatchEx *dispex, *dispex_ie9;
     IHTMLDocument2 *doc, *doc_ie9;
     IHTMLElement *elem, *elem_ie9;
+    IOleDocumentView *doc_view;
     IPersistStreamInit *init;
     DISPPARAMS dp = { 0 };
     IHTMLDocument6 *doc6;
@@ -13628,6 +13630,8 @@ static void test_attribute_node_across_modes(void)
         TranslateMessage(&msg);
         DispatchMessageW(&msg);
     }
+    doc_view = view;
+    view = NULL;
 
     notif_doc = doc_ie9 = create_document();
     if(!doc_ie9) {
@@ -13827,6 +13831,8 @@ end:
 
     set_client_site(doc_ie9, FALSE);
     IHTMLDocument2_Release(doc_ie9);
+
+    view = doc_view;
     set_client_site(doc, FALSE);
     IHTMLDocument2_Release(doc);
 }
diff --git a/dlls/msnet32/Makefile.in b/dlls/msnet32/Makefile.in
index fe806e501bf0..e7a291aa302f 100644
--- a/dlls/msnet32/Makefile.in
+++ b/dlls/msnet32/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = msnet32.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	msnet_main.c
diff --git a/dlls/msports/Makefile.in b/dlls/msports/Makefile.in
index 45cfdb9f23e8..f51e4e01b6f0 100644
--- a/dlls/msports/Makefile.in
+++ b/dlls/msports/Makefile.in
@@ -1,6 +1,4 @@
 MODULE    = msports.dll
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	msports.c
diff --git a/dlls/mssip32/Makefile.in b/dlls/mssip32/Makefile.in
index 3ab53d6f6fb6..fa1c63849b2d 100644
--- a/dlls/mssip32/Makefile.in
+++ b/dlls/mssip32/Makefile.in
@@ -1,3 +1 @@
 MODULE    = mssip32.dll
-
-EXTRADLLFLAGS = -Wb,--prefer-native
diff --git a/dlls/msv1_0/main.c b/dlls/msv1_0/main.c
index 985b83b327ba..494aac05c72e 100644
--- a/dlls/msv1_0/main.c
+++ b/dlls/msv1_0/main.c
@@ -1645,6 +1645,7 @@ static NTSTATUS verify_signature( struct ntlm_ctx *ctx, unsigned int flags, SecB
     }
 
     free( buf );
+    if (status != SEC_E_OK) TRACE( "signature verification failed %lx\n", status );
     return status;
 }
 
@@ -1708,7 +1709,7 @@ static NTSTATUS NTAPI ntlm_SpSealMessage( LSA_SEC_HANDLE handle, ULONG qop, SecB
 
 static NTSTATUS NTAPI ntlm_SpUnsealMessage( LSA_SEC_HANDLE handle, SecBufferDesc *msg, ULONG msg_seq_no, ULONG *qop )
 {
-    int data_idx, stream_idx, token_idx;
+    int i, data_idx, stream_idx, token_idx;
     SecBuffer token_buf;
     struct ntlm_ctx *ctx;
 
@@ -1745,11 +1746,23 @@ static NTSTATUS NTAPI ntlm_SpUnsealMessage( LSA_SEC_HANDLE handle, SecBufferDesc
 
     ctx = (struct ntlm_ctx *)handle;
     if (ctx->flags & FLAG_NEGOTIATE_NTLM2 && ctx->flags & FLAG_NEGOTIATE_SEAL)
-        arc4_process( &ctx->crypt.ntlm2.recv_arc4info, msg->pBuffers[data_idx].pvBuffer,
-                      msg->pBuffers[data_idx].cbBuffer );
+    {
+        for (i = 0; i < msg->cBuffers; i++)
+        {
+            if (msg->pBuffers[i].BufferType != SECBUFFER_DATA) continue;
+            arc4_process( &ctx->crypt.ntlm2.recv_arc4info, msg->pBuffers[i].pvBuffer,
+                          msg->pBuffers[i].cbBuffer );
+        }
+    }
     else
-        arc4_process( &ctx->crypt.ntlm.arc4info, msg->pBuffers[data_idx].pvBuffer,
-                      msg->pBuffers[data_idx].cbBuffer);
+    {
+        for (i = 0; i < msg->cBuffers; i++)
+        {
+            if (msg->pBuffers[i].BufferType != SECBUFFER_DATA) continue;
+            arc4_process( &ctx->crypt.ntlm.arc4info, msg->pBuffers[i].pvBuffer,
+                          msg->pBuffers[i].cbBuffer);
+        }
+    }
 
     /* make sure we use a session key for the signature check, SealMessage always does that,
        even in the dummy case */
diff --git a/dlls/msvcrt/except_i386.c b/dlls/msvcrt/except_i386.c
index 26cd2926fca4..2d56ce4e6d63 100644
--- a/dlls/msvcrt/except_i386.c
+++ b/dlls/msvcrt/except_i386.c
@@ -130,10 +130,16 @@ extern int call_filter( int (*func)(PEXCEPTION_POINTERS), void *arg, void *ebp )
 
 __ASM_GLOBAL_FUNC( call_filter,
                    "pushl %ebp\n\t"
-                   "pushl 12(%esp)\n\t"
-                   "movl 20(%esp), %ebp\n\t"
-                   "call *12(%esp)\n\t"
+                   "pushl %ebx\n\t"
+                   "pushl %esi\n\t"
+                   "pushl %edi\n\t"
+                   "pushl 24(%esp)\n\t"
+                   "movl 32(%esp), %ebp\n\t"
+                   "call *24(%esp)\n\t"
                    "popl %ebp\n\t"
+                   "popl %edi\n\t"
+                   "popl %esi\n\t"
+                   "popl %ebx\n\t"
                    "popl %ebp\n\t"
                    "ret" );
 
diff --git a/dlls/msxml3/node.c b/dlls/msxml3/node.c
index e72ebc9132c4..4c2fda32e750 100644
--- a/dlls/msxml3/node.c
+++ b/dlls/msxml3/node.c
@@ -4865,7 +4865,9 @@ WCHAR *xpath_translate_function(const WCHAR *to, const WCHAR *from, const WCHAR
         cptr++;
     }
 
-    if (buffer.status != S_OK)
+    string_append(&buffer, L"", 1);
+
+    if (buffer._status != S_OK)
         string_buffer_cleanup(&buffer);
 
     return buffer.data;
diff --git a/dlls/msxml3/tests/domdoc.c b/dlls/msxml3/tests/domdoc.c
index 5464a76aa383..9254914ca07e 100644
--- a/dlls/msxml3/tests/domdoc.c
+++ b/dlls/msxml3/tests/domdoc.c
@@ -5280,6 +5280,7 @@ static const struct query_test xpath_test[] =
     { "*/c", "E3.E1.E2.D1 E3.E2.E2.D1" },
     { "*/d", "E4.E1.E2.D1 E4.E2.E2.D1 E4.E4.E2.D1" },
     { "//a", "E1.E1.E2.D1 E1.E2.E2.D1 E1.E4.E2.D1" },
+    { "//elem[translate(a, \"A\", \"a\") = \"a1 field\"]", "E1.E2.D1" },
     { "//b", "E2.E1.E2.D1 E2.E2.E2.D1 E2.E4.E2.D1" },
     { "//c", "E3.E1.E2.D1 E3.E2.E2.D1" },
     { "//d", "E4.E1.E2.D1 E4.E2.E2.D1 E4.E4.E2.D1" },
diff --git a/dlls/msxml3/xpath.c b/dlls/msxml3/xpath.c
index 2f5513d8a65b..f2cb685e5c99 100644
--- a/dlls/msxml3/xpath.c
+++ b/dlls/msxml3/xpath.c
@@ -2535,6 +2535,13 @@ static int xpath_equal_number_values(double arg1, double arg2)
     return ret;
 }
 
+static bool xpath_strequal(const WCHAR *str1, const WCHAR *str2)
+{
+    if (str1 == str2) return true;
+    if (!str1 || !str2) return false;
+    return !wcscmp(str1, str2);
+}
+
 static bool xpath_equal_values_common(struct xpath_parser_context *ctxt,
         struct xpath_object *arg1, struct xpath_object *arg2)
 {
@@ -2588,7 +2595,7 @@ static bool xpath_equal_values_common(struct xpath_parser_context *ctxt,
                     ret = (arg2->boolval == ret);
                     break;
                 case XPATH_STRING:
-                    ret = !wcscmp(arg1->stringval, arg2->stringval);
+                    ret = xpath_strequal(arg1->stringval, arg2->stringval);
                     break;
                 case XPATH_NUMBER:
                     xpath_push_value(ctxt, arg1);
@@ -6044,6 +6051,14 @@ static void xpath_builtin_concat(struct xpath_parser_context *ctxt, int nargs)
     xpath_push_value(ctxt, cur);
 }
 
+static WCHAR * xpath_wcsstr(const WCHAR *str, const WCHAR *sub)
+{
+    if (!str || !sub)
+        return NULL;
+
+    return wcsstr(str, sub);
+}
+
 static void xpath_builtin_contains(struct xpath_parser_context *ctxt, int nargs)
 {
     struct xpath_object *hay, *needle;
@@ -6061,7 +6076,7 @@ static void xpath_builtin_contains(struct xpath_parser_context *ctxt, int nargs)
 
     if (hay && hay->type == _XPATH_STRING)
     {
-        match = wcsstr(hay->stringval, needle->stringval);
+        match = !!xpath_wcsstr(hay->stringval, needle->stringval);
         xpath_push_value(ctxt, xpath_new_boolean(ctxt, match));
         xpath_object_release(ctxt->context, hay);
         xpath_object_release(ctxt->context, needle);
@@ -6605,7 +6620,7 @@ static void xpath_builtin_substringbefore(struct xpath_parser_context *ctxt, int
     xpath_arg_cast_to_string(ctxt);
     str = xpath_pop_value(ctxt);
 
-    point = wcsstr(str->stringval, find->stringval);
+    point = xpath_wcsstr(str->stringval, find->stringval);
     if (point)
     {
         offset = point - str->stringval;
@@ -6632,7 +6647,7 @@ static void xpath_builtin_substringafter(struct xpath_parser_context *ctxt, int
     xpath_arg_cast_to_string(ctxt);
     str = xpath_pop_value(ctxt);
 
-    point = wcsstr(str->stringval, find->stringval);
+    point = xpath_wcsstr(str->stringval, find->stringval);
     if (point)
     {
         offset = point - str->stringval + wcslen(find->stringval);
diff --git a/dlls/netprofm/Makefile.in b/dlls/netprofm/Makefile.in
index 46af13bb74c0..488a79ca0c8e 100644
--- a/dlls/netprofm/Makefile.in
+++ b/dlls/netprofm/Makefile.in
@@ -1,8 +1,6 @@
 MODULE    = netprofm.dll
 IMPORTS   = iphlpapi
 
-EXTRADLLFLAGS = -Wb,--prefer-native
-
 SOURCES = \
 	list.c \
 	main.c \
diff --git a/dlls/ntdll/signal_arm.c b/dlls/ntdll/signal_arm.c
index fe0e86001a63..3dcbd422e4ab 100644
--- a/dlls/ntdll/signal_arm.c
+++ b/dlls/ntdll/signal_arm.c
@@ -520,9 +520,10 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec
         }
         else  /* hack: call builtin handlers registered in the tib list */
         {
-            while (is_valid_frame( (ULONG_PTR)teb_frame ) &&
-                   (DWORD)teb_frame < new_context.Sp &&
-                   (DWORD)teb_frame < (DWORD)end_frame)
+            ULONG_PTR last_frame = new_context.Sp;
+            if (end_frame && (ULONG_PTR)end_frame < last_frame) last_frame = (ULONG_PTR)end_frame;
+
+            while (is_valid_frame( (ULONG_PTR)teb_frame ) && (ULONG_PTR)teb_frame < last_frame)
             {
                 TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
                        teb_frame->Handler, rec, teb_frame, dispatch.ContextRecord, &dispatch );
@@ -549,7 +550,7 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec
                     break;
                 }
             }
-            if ((DWORD)teb_frame == (DWORD)end_frame && (DWORD)end_frame < new_context.Sp) break;
+            if ((ULONG_PTR)teb_frame == last_frame && last_frame < new_context.Sp) break;
         }
 
         if (dispatch.EstablisherFrame == (DWORD)end_frame) break;
diff --git a/dlls/ntdll/signal_arm64.c b/dlls/ntdll/signal_arm64.c
index 056f8750ece2..e560718c7705 100644
--- a/dlls/ntdll/signal_arm64.c
+++ b/dlls/ntdll/signal_arm64.c
@@ -557,9 +557,10 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec
         }
         else  /* hack: call builtin handlers registered in the tib list */
         {
-            while (is_valid_frame( (ULONG_PTR)teb_frame ) &&
-                   (ULONG64)teb_frame < new_context.Sp &&
-                   (ULONG64)teb_frame < (ULONG64)end_frame)
+            ULONG_PTR last_frame = new_context.Sp;
+            if (end_frame && (ULONG_PTR)end_frame < last_frame) last_frame = (ULONG_PTR)end_frame;
+
+            while (is_valid_frame( (ULONG_PTR)teb_frame ) && (ULONG_PTR)teb_frame < last_frame)
             {
                 TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
                        teb_frame->Handler, rec, teb_frame, dispatch.ContextRecord, &dispatch );
@@ -586,7 +587,7 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec
                     break;
                 }
             }
-            if ((ULONG64)teb_frame == (ULONG64)end_frame && (ULONG64)end_frame < new_context.Sp) break;
+            if ((ULONG_PTR)teb_frame == last_frame && last_frame < new_context.Sp) break;
         }
 
         if (dispatch.EstablisherFrame == (ULONG64)end_frame) break;
diff --git a/dlls/ntdll/signal_x86_64.c b/dlls/ntdll/signal_x86_64.c
index 29fadc722425..b94e4a23a99c 100644
--- a/dlls/ntdll/signal_x86_64.c
+++ b/dlls/ntdll/signal_x86_64.c
@@ -775,9 +775,10 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec
         }
         else  /* hack: call builtin handlers registered in the tib list */
         {
-            while (is_valid_frame( (ULONG_PTR)teb_frame ) &&
-                   (ULONG64)teb_frame < new_context.Rsp &&
-                   (ULONG64)teb_frame < (ULONG64)end_frame)
+            ULONG_PTR last_frame = new_context.Rsp;
+            if (end_frame && (ULONG_PTR)end_frame < last_frame) last_frame = (ULONG_PTR)end_frame;
+
+            while (is_valid_frame( (ULONG_PTR)teb_frame ) && (ULONG_PTR)teb_frame < last_frame)
             {
                 TRACE( "calling TEB handler %p (rec=%p, frame=%p context=%p, dispatch=%p)\n",
                        teb_frame->Handler, rec, teb_frame, dispatch.ContextRecord, &dispatch );
@@ -805,7 +806,7 @@ void WINAPI RtlUnwindEx( PVOID end_frame, PVOID target_ip, EXCEPTION_RECORD *rec
                     break;
                 }
             }
-            if ((ULONG64)teb_frame == (ULONG64)end_frame && (ULONG64)end_frame < new_context.Rsp) break;
+            if ((ULONG_PTR)teb_frame == last_frame && last_frame < new_context.Rsp) break;
         }
 
         if (dispatch.EstablisherFrame == (ULONG64)end_frame) break;
diff --git a/dlls/ntdll/unix/system.c b/dlls/ntdll/unix/system.c
index cf2883f05aee..2d6e3184ac9a 100644
--- a/dlls/ntdll/unix/system.c
+++ b/dlls/ntdll/unix/system.c
@@ -24,6 +24,7 @@
 
 #include "config.h"
 
+#include <assert.h>
 #include <fcntl.h>
 #include <string.h>
 #include <stdarg.h>
@@ -36,6 +37,9 @@
 #include <sys/time.h>
 #include <time.h>
 #include <dirent.h>
+#ifdef HAVE_ASM_HWCAP_H
+# include <asm/hwcap.h>
+#endif
 #ifdef HAVE_SYS_PARAM_H
 # include <sys/param.h>
 #endif
@@ -73,6 +77,58 @@
 # include <hwloc.h>
 #endif
 
+#ifdef __linux__
+# define AT_HWCAP2          26
+#endif
+
+#if defined(__aarch64__) && defined(AT_HWCAP)
+# define HWCAP_AES          (1 << 3)
+# define HWCAP_CRC32        (1 << 7)
+# define HWCAP_ATOMICS      (1 << 8)
+# define HWCAP_FPHP         (1 << 9)
+# define HWCAP_CPUID        (1 << 11)
+# define HWCAP_JSCVT        (1 << 13)
+# define HWCAP_LRCPC        (1 << 15)
+# define HWCAP_SHA3         (1 << 17)
+# define HWCAP_ASIMDDP      (1 << 20)
+# define HWCAP_SHA512       (1 << 21)
+# define HWCAP_SVE          (1 << 22)
+# define HWCAP_USCAT        (1 << 25)
+# define HWCAP_SME2P2       (1UL << 42)
+# define HWCAP_SME_SBITPERM (1UL << 43)
+# define HWCAP_SME_AES      (1UL << 44)
+# define HWCAP2_SVE2        (1 << 1)
+# define HWCAP2_SVEAES      (1 << 2)
+# define HWCAP2_SVEPMULL    (1 << 3)
+# define HWCAP2_SVEBITPERM  (1 << 4)
+# define HWCAP2_SVESHA3     (1 << 5)
+# define HWCAP2_SVESM4      (1 << 6)
+# define HWCAP2_SVEI8MM     (1 << 9)
+# define HWCAP2_SVEF32MM    (1 << 10)
+# define HWCAP2_SVEF64MM    (1 << 11)
+# define HWCAP2_SVEBF16     (1 << 12)
+# define HWCAP2_I8MM        (1 << 13)
+# define HWCAP2_BF16        (1 << 14)
+# define HWCAP2_SME         (1 << 23)
+# define HWCAP2_SME_I16I64  (1 << 24)
+# define HWCAP2_SME_F64F64  (1 << 25)
+# define HWCAP2_SME_FA64    (1 << 30)
+# define HWCAP2_EBF16       (1UL << 32)
+# define HWCAP2_SVE_EBF16   (1UL << 33)
+# define HWCAP2_SVE2P1      (1UL << 36)
+# define HWCAP2_SME2        (1UL << 37)
+# define HWCAP2_SME2P1      (1UL << 38)
+# define HWCAP2_SME_F16F16  (1UL << 42)
+# define HWCAP2_SME_B16B16  (1UL << 41)
+# define HWCAP2_SVE_B16B16  (1UL << 45)
+# define HWCAP2_SME_LUTV2   (1UL << 57)
+# define HWCAP2_SME_F8F16   (1UL << 58)
+# define HWCAP2_SME_F8F32   (1UL << 59)
+# define HWCAP2_SME_SF8FMA  (1UL << 60)
+# define HWCAP2_SME_SF8DP4  (1UL << 61)
+# define HWCAP2_SME_SF8DP2  (1UL << 62)
+#endif
+
 #include "ntstatus.h"
 #include "windef.h"
 #include "winternl.h"
@@ -523,7 +579,7 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data )
         features[PF_AVX512F_INSTRUCTIONS_AVAILABLE] = !!(regs[1] & (1 << 16));
         features[PF_RDPID_INSTRUCTION_AVAILABLE]    = !!(regs[2] & (1 << 22));
         features[PF_MOVDIR64B_INSTRUCTION_AVAILABLE]= !!(regs[2] & (1 << 28));
-#if defined(__linux__) && defined(AT_HWCAP2)
+#if defined(__linux__)
         features[PF_RDWRFSGSBASE_AVAILABLE] &= !!(getauxval( AT_HWCAP2 ) & 2);
 #endif
     }
@@ -545,18 +601,67 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data )
 
 #elif defined(__arm__) || defined(__aarch64__)
 
-static int has_feature( const char *line, const char *feat )
+#if defined(AT_HWCAP)
+static BOOLEAN has_capability( int hwcap, unsigned long hwcap_bit )
+{
+    unsigned long type;
+    switch (hwcap)
+    {
+    case 1: type = AT_HWCAP;  break;
+    case 2: type = AT_HWCAP2; break;
+    default: return FALSE;
+    }
+    return !!(getauxval( type ) & hwcap_bit);
+}
+
+#define HAS_FEATURE(hwcap, hwcap_bit, ...) has_capability( hwcap, hwcap_bit )
+#elif defined(__APPLE__)
+static BOOLEAN has_feature( const char *feature )
+{
+    char buf[200];
+    int val;
+    size_t size = sizeof(val);
+
+    snprintf( buf, sizeof(buf), "hw.optional.arm.%s", feature );
+    if (!sysctlbyname( buf, &val, &size, NULL, 0 ))
+        return !!val;
+    return FALSE;
+}
+
+static BOOLEAN has_features( int dummy, ... )
 {
-    size_t len = strlen(feat);
+    BOOLEAN ret = TRUE;
+    const char *feature;
+    va_list args;
 
-    while (*line)
+    va_start( args, dummy );
+
+    while ((feature = va_arg( args, const char * )))
     {
-        while (*line == ' ' || *line == '\t') line++;
-        if (!strncmp( line, feat, len ) && (!line[len] || isspace(line[len]))) return 1;
-        while (*line && *line != ' ' && *line != '\t') line++;
+        if (has_feature( feature ))
+            continue;
+
+        ret = FALSE;
+        break;
     }
-    return 0;
+
+    va_end( args );
+    return ret;
+}
+#define HAS_FEATURE(hwcap, hwcap_bit, ...) has_features( 0, __VA_ARGS__, NULL )
+#else
+#define HAS_FEATURE(hwcap, hwcap_bit, ...) 0
+#endif
+
+#ifdef __aarch64__
+static void set_feature_bitmap( ULONG flag, BOOLEAN enabled )
+{
+    assert( flag >= PROCESSOR_FEATURE_MAX );
+    if (!enabled) return;
+    flag -= PROCESSOR_FEATURE_MAX;
+    cpu_features_bitmap[flag / 64] |= 1ull << (flag % 64);
 }
+#endif
 
 static void init_cpu_model(void)
 {
@@ -583,46 +688,6 @@ static void init_cpu_model(void)
             else if (!strcmp( line, "CPU part" )) part = strtoul( value, NULL, 0);
             else if (!strcmp( line, "CPU variant" )) variant = strtoul( value, NULL, 0);
             else if (!strcmp( line, "CPU revision" )) revision = strtoul( value, NULL, 0);
-            else if (!strcmp( line, "Features" ))
-            {
-                static const struct { ULONG flag; const char *name; } features[] =
-                {
-                    { PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE, "sha3" },
-                    { PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE, "sha512" },
-                    { PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE, "i8mm" },
-                    { PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE, "fphp" },
-                    { PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE, "bf16" },
-                    { PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE, "ebf16" },
-                    { PF_ARM_SME_INSTRUCTIONS_AVAILABLE, "sme" },
-                    { PF_ARM_SME2_INSTRUCTIONS_AVAILABLE, "sme2" },
-                    { PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE, "sme2p1" },
-                    { PF_ARM_SME2_2_INSTRUCTIONS_AVAILABLE, "sme2p2" },
-                    { PF_ARM_SME_AES_INSTRUCTIONS_AVAILABLE, "smeaes" },
-                    { PF_ARM_SME_SBITPERM_INSTRUCTIONS_AVAILABLE, "smesbitperm" },
-                    /* The PF_ARM_SME_SF8MM4_INSTRUCTIONS_AVAILABLE and
-                     * PF_ARM_SME_SF8MM8_INSTRUCTIONS_AVAILABLE flags aren't exposed by
-                     * the Linux kernel, see
-                     * https://lists.infradead.org/pipermail/linux-arm-kernel/2025-January/991187.html */
-                    { PF_ARM_SME_SF8DP2_INSTRUCTIONS_AVAILABLE, "smesf8dp2" },
-                    { PF_ARM_SME_SF8DP4_INSTRUCTIONS_AVAILABLE, "smesf8dp4" },
-                    { PF_ARM_SME_SF8FMA_INSTRUCTIONS_AVAILABLE, "smesf8fma" },
-                    { PF_ARM_SME_F8F32_INSTRUCTIONS_AVAILABLE, "smef8f32" },
-                    { PF_ARM_SME_F8F16_INSTRUCTIONS_AVAILABLE, "smef8f16" },
-                    { PF_ARM_SME_F16F16_INSTRUCTIONS_AVAILABLE, "smef16f16" },
-                    { PF_ARM_SME_B16B16_INSTRUCTIONS_AVAILABLE, "smeb16b16" },
-                    { PF_ARM_SME_F64F64_INSTRUCTIONS_AVAILABLE, "smef64f64" },
-                    { PF_ARM_SME_I16I64_INSTRUCTIONS_AVAILABLE, "smei16i64" },
-                    { PF_ARM_SME_LUTv2_INSTRUCTIONS_AVAILABLE, "smelutv2" },
-                    { PF_ARM_SME_FA64_INSTRUCTIONS_AVAILABLE, "smefa64" },
-                };
-
-                for (unsigned int i = 0; i < ARRAY_SIZE(features); i++)
-                {
-                    ULONG flag = features[i].flag - PROCESSOR_FEATURE_MAX;
-                    if (!has_feature( value, features[i].name )) continue;
-                    cpu_features_bitmap[flag / 64] |= 1ull << (flag % 64);
-                }
-            }
         }
         fclose( f );
     }
@@ -644,6 +709,38 @@ static void init_cpu_model(void)
     case 0x66: strcpy( cpu_vendor, "Faraday" ); break;
     case 0x69: strcpy( cpu_vendor, "Intel" ); break;
     }
+
+#ifdef __aarch64__
+    set_feature_bitmap( PF_ARM_SHA3_INSTRUCTIONS_AVAILABLE,      HAS_FEATURE( 1, HWCAP_SHA3, "FEAT_SHA3" ) );
+    set_feature_bitmap( PF_ARM_SHA512_INSTRUCTIONS_AVAILABLE,    HAS_FEATURE( 1, HWCAP_SHA512, "FEAT_SHA512" ) );
+    set_feature_bitmap( PF_ARM_V82_I8MM_INSTRUCTIONS_AVAILABLE,  HAS_FEATURE( 2, HWCAP2_I8MM, "FEAT_I8MM" ) );
+    set_feature_bitmap( PF_ARM_V82_FP16_INSTRUCTIONS_AVAILABLE,  HAS_FEATURE( 1, HWCAP_FPHP, "FEAT_FP16" ) );
+    set_feature_bitmap( PF_ARM_V86_BF16_INSTRUCTIONS_AVAILABLE,  HAS_FEATURE( 2, HWCAP2_BF16, "FEAT_BF16" ) );
+    set_feature_bitmap( PF_ARM_V86_EBF16_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_EBF16, "FEAT_EBF16" ) );
+    set_feature_bitmap( PF_ARM_SME_INSTRUCTIONS_AVAILABLE,       HAS_FEATURE( 2, HWCAP2_SME, "FEAT_SME" ) );
+    set_feature_bitmap( PF_ARM_SME2_INSTRUCTIONS_AVAILABLE,      HAS_FEATURE( 2, HWCAP2_SME2, "FEAT_SME2" ) );
+    set_feature_bitmap( PF_ARM_SME2_1_INSTRUCTIONS_AVAILABLE,    HAS_FEATURE( 2, HWCAP2_SME2P1, "FEAT_SME2p1" ) );
+    set_feature_bitmap( PF_ARM_SME2_2_INSTRUCTIONS_AVAILABLE,    HAS_FEATURE( 1, HWCAP_SME2P2, "FEAT_SME2p2" ) );
+    set_feature_bitmap( PF_ARM_SME_AES_INSTRUCTIONS_AVAILABLE,   HAS_FEATURE( 1, HWCAP_SME_AES, "FEAT_SSVE_AES" ) );
+    set_feature_bitmap( PF_ARM_SME_SBITPERM_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 1, HWCAP_SME_SBITPERM, "FEAT_SSVE_BitPerm" ) );
+    /* The PF_ARM_SME_SF8MM4_INSTRUCTIONS_AVAILABLE and
+     * PF_ARM_SME_SF8MM8_INSTRUCTIONS_AVAILABLE flags aren't exposed by
+     * the Linux kernel, see
+     * https://lists.infradead.org/pipermail/linux-arm-kernel/2025-January/991187.html */
+    set_feature_bitmap( PF_ARM_SME_SF8MM4_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 0, 0, "FEAT_SSVE_F8F16MM" ) );
+    set_feature_bitmap( PF_ARM_SME_SF8MM8_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 0, 0, "FEAT_SSVE_F8F32MM" ) );
+    set_feature_bitmap( PF_ARM_SME_SF8DP2_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_SF8DP2, "FEAT_SSVE_FP8DOT2" ) );
+    set_feature_bitmap( PF_ARM_SME_SF8DP4_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_SF8DP4, "FEAT_SSVE_FP8DOT4" ) );
+    set_feature_bitmap( PF_ARM_SME_SF8FMA_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_SF8FMA, "FEAT_SSVE_FP8FMA" ) );
+    set_feature_bitmap( PF_ARM_SME_F8F32_INSTRUCTIONS_AVAILABLE,  HAS_FEATURE( 2, HWCAP2_SME_F8F32, "FEAT_SME_F8F32" ) );
+    set_feature_bitmap( PF_ARM_SME_F8F16_INSTRUCTIONS_AVAILABLE,  HAS_FEATURE( 2, HWCAP2_SME_F8F16, "FEAT_SME_F8F16" ) );
+    set_feature_bitmap( PF_ARM_SME_F16F16_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_F16F16, "FEAT_SME_F16F16" ) );
+    set_feature_bitmap( PF_ARM_SME_B16B16_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_B16B16, "FEAT_SME_B16B16" ) );
+    set_feature_bitmap( PF_ARM_SME_F64F64_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_F64F64, "FEAT_SME_F64F64" ) );
+    set_feature_bitmap( PF_ARM_SME_I16I64_INSTRUCTIONS_AVAILABLE, HAS_FEATURE( 2, HWCAP2_SME_I16I64, "FEAT_SME_I16I64" ) );
+    set_feature_bitmap( PF_ARM_SME_LUTv2_INSTRUCTIONS_AVAILABLE,  HAS_FEATURE( 2, HWCAP2_SME_LUTV2, "FEAT_SME_LUTv2" ) );
+    set_feature_bitmap( PF_ARM_SME_FA64_INSTRUCTIONS_AVAILABLE,   HAS_FEATURE( 2, HWCAP2_SME_FA64, "FEAT_SME_FA64" ) );
+#endif
 }
 
 static ULONGLONG get_cpu_features(void)
@@ -655,71 +752,48 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data )
 {
     BOOLEAN *features = data->ProcessorFeatures;
 
-#ifdef linux
-    FILE *f = fopen("/proc/cpuinfo", "r");
-    if (f)
-    {
-        char *s, *value, line[512];
-        while (fgets( line, sizeof(line), f ))
-        {
-            /* NOTE: the ':' is the only character we can rely on */
-            if (!(value = strchr(line,':'))) continue;
-            /* terminate the valuename */
-            s = value - 1;
-            while ((s >= line) && (*s == ' ' || *s == '\t')) s--;
-            s[1] = 0;
-            value++;
-            if ((s = strchr( value, '\n' ))) *s = 0;
-            if (strcmp( line, "Features" )) continue;
-            features[PF_ARM_VFP_32_REGISTERS_AVAILABLE]          = has_feature( value, "vfpv3" );
-            features[PF_ARM_NEON_INSTRUCTIONS_AVAILABLE]         = has_feature( value, "neon" );
-            features[PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE]        = has_feature( value, "idivt" );
-            if (native_machine == IMAGE_FILE_MACHINE_ARMNT) break;
-            features[PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE]     = has_feature( value, "crc32" );
-            features[PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE]    = has_feature( value, "aes" );
-            features[PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE]   = has_feature( value, "atomics" );
-            features[PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE]       = has_feature( value, "asimddp" );
-            features[PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE]    = has_feature( value, "jscvt" );
-            features[PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE]    = has_feature( value, "lrcpc" );
-            features[PF_ARM_SVE_INSTRUCTIONS_AVAILABLE]          = has_feature( value, "sve" );
-            features[PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE]         = has_feature( value, "sve2" );
-            features[PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE]       = has_feature( value, "sve2p1" );
-            features[PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE]      = has_feature( value, "sveaes" );
-            features[PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE] = has_feature( value, "svepmull" );
-            features[PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE]  = has_feature( value, "svebitperm" );
-            features[PF_ARM_SVE_BF16_INSTRUCTIONS_AVAILABLE]     = has_feature( value, "svebf16" );
-            features[PF_ARM_SVE_EBF16_INSTRUCTIONS_AVAILABLE]    = has_feature( value, "sveebf16" );
-            features[PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE]   = has_feature( value, "sveb16b16" );
-            features[PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE]     = has_feature( value, "svesha3" );
-            features[PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE]      = has_feature( value, "svesm4" );
-            features[PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE]     = has_feature( value, "svei8mm" );
-            features[PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE]    = has_feature( value, "svef32mm" );
-            features[PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE]    = has_feature( value, "svef64mm" );
-            features[PF_ARM_LSE2_AVAILABLE]                      = has_feature( value, "uscat" );
-            break;
-        }
-        fclose( f );
-    }
-#endif
-
     features[PF_FASTFAIL_AVAILABLE]      = TRUE;
     features[PF_COMPARE_EXCHANGE_DOUBLE] = TRUE;
 
-    if (native_machine == IMAGE_FILE_MACHINE_ARMNT) return;
-
-    features[PF_ARM_V8_INSTRUCTIONS_AVAILABLE] = TRUE;
-    features[PF_NX_ENABLED]                    = TRUE;
+#ifdef __arm__
+    features[PF_ARM_VFP_32_REGISTERS_AVAILABLE]          = HAS_FEATURE( 1, HWCAP_VFPv3 );
+    features[PF_ARM_NEON_INSTRUCTIONS_AVAILABLE]         = HAS_FEATURE( 1, HWCAP_NEON );
+    features[PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE]        = HAS_FEATURE( 1, HWCAP_IDIVT );
+#else
+    features[PF_ARM_VFP_32_REGISTERS_AVAILABLE]          = TRUE;
+    features[PF_ARM_NEON_INSTRUCTIONS_AVAILABLE]         = TRUE;
+    features[PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE]        = TRUE;
+    features[PF_ARM_64BIT_LOADSTORE_ATOMIC]              = TRUE;
+    features[PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE]         = TRUE;
+    features[PF_ARM_V8_INSTRUCTIONS_AVAILABLE]           = TRUE;
+    features[PF_NX_ENABLED]                              = TRUE;
+    features[PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE]    = HAS_FEATURE( 1, HWCAP_AES, "FEAT_AES" );
+    features[PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE]     = HAS_FEATURE( 1, HWCAP_CRC32, "FEAT_CRC32" );
+    features[PF_ARM_V81_ATOMIC_INSTRUCTIONS_AVAILABLE]   = HAS_FEATURE( 1, HWCAP_ATOMICS, "FEAT_LSE" );
+    features[PF_ARM_V82_DP_INSTRUCTIONS_AVAILABLE]       = HAS_FEATURE( 1, HWCAP_ASIMDDP, "FEAT_DotProd" );
+    features[PF_ARM_V83_JSCVT_INSTRUCTIONS_AVAILABLE]    = HAS_FEATURE( 1, HWCAP_JSCVT, "FEAT_JSCVT" );
+    features[PF_ARM_V83_LRCPC_INSTRUCTIONS_AVAILABLE]    = HAS_FEATURE( 1, HWCAP_LRCPC, "FEAT_LRCPC" );
+    features[PF_ARM_SVE_INSTRUCTIONS_AVAILABLE]          = HAS_FEATURE( 1, HWCAP_SVE, "FEAT_SVE" );
+    features[PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE]         = HAS_FEATURE( 2, HWCAP2_SVE2, "FEAT_SVE2" );
+    features[PF_ARM_SVE2_1_INSTRUCTIONS_AVAILABLE]       = HAS_FEATURE( 2, HWCAP2_SVE2P1, "FEAT_SVE2p1" );
+    features[PF_ARM_SVE_AES_INSTRUCTIONS_AVAILABLE]      = HAS_FEATURE( 2, HWCAP2_SVEAES, "FEAT_SVE_AES" );
+    features[PF_ARM_SVE_PMULL128_INSTRUCTIONS_AVAILABLE] = HAS_FEATURE( 2, HWCAP2_SVEPMULL, "FEAT_SVE_PMULL128" );
+    features[PF_ARM_SVE_BITPERM_INSTRUCTIONS_AVAILABLE]  = HAS_FEATURE( 2, HWCAP2_SVEBITPERM, "FEAT_SVE_BitPerm" );
+    features[PF_ARM_SVE_BF16_INSTRUCTIONS_AVAILABLE]     = HAS_FEATURE( 2, HWCAP2_SVEBF16, "FEAT_SVE", "FEAT_BF16" );
+    features[PF_ARM_SVE_EBF16_INSTRUCTIONS_AVAILABLE]    = HAS_FEATURE( 2, HWCAP2_SVE_EBF16, "FEAT_SVE", "FEAT_EBF16" );
+    features[PF_ARM_SVE_B16B16_INSTRUCTIONS_AVAILABLE]   = HAS_FEATURE( 2, HWCAP2_SVE_B16B16, "FEAT_SVE_B16B16" );
+    features[PF_ARM_SVE_SHA3_INSTRUCTIONS_AVAILABLE]     = HAS_FEATURE( 2, HWCAP2_SVESHA3, "FEAT_SVE_SHA3" );
+    features[PF_ARM_SVE_SM4_INSTRUCTIONS_AVAILABLE]      = HAS_FEATURE( 2, HWCAP2_SVESM4, "FEAT_SVE_SM4" );
+    features[PF_ARM_SVE_I8MM_INSTRUCTIONS_AVAILABLE]     = HAS_FEATURE( 2, HWCAP2_SVEI8MM, "FEAT_SVE", "FEAT_I8MM" );
+    features[PF_ARM_SVE_F32MM_INSTRUCTIONS_AVAILABLE]    = HAS_FEATURE( 2, HWCAP2_SVEF32MM, "FEAT_F32MM" );
+    features[PF_ARM_SVE_F64MM_INSTRUCTIONS_AVAILABLE]    = HAS_FEATURE( 2, HWCAP2_SVEF64MM, "FEAT_F64MM" );
+    features[PF_ARM_LSE2_AVAILABLE]                      = HAS_FEATURE( 1, HWCAP_USCAT, "FEAT_LSE2" );
 
     /* add features for other architectures supported by wow64 */
     for (unsigned int i = 0; i < supported_machines_count; i++)
     {
         switch (supported_machines[i])
         {
-        case IMAGE_FILE_MACHINE_ARMNT:
-            features[PF_ARM_VFP_32_REGISTERS_AVAILABLE]   = TRUE;
-            features[PF_ARM_NEON_INSTRUCTIONS_AVAILABLE]  = TRUE;
-            features[PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE] = TRUE;
-            break;
         case IMAGE_FILE_MACHINE_I386:
             features[PF_MMX_INSTRUCTIONS_AVAILABLE]    = TRUE;
             features[PF_XMMI_INSTRUCTIONS_AVAILABLE]   = TRUE;
@@ -734,6 +808,7 @@ void init_shared_data_cpuinfo( KUSER_SHARED_DATA *data )
             break;
         }
     }
+#endif
 }
 
 #endif /* End architecture specific feature detection for CPUs */
@@ -1946,8 +2021,6 @@ static WORD append_smbios_boot_info( struct smbios_buffer *buf )
 #ifdef __aarch64__
 #ifdef linux
 
-#include <asm/hwcap.h>
-
 static DWORD get_core_id_regs_arm64( struct smbios_wine_id_reg_value_arm64 *regs,
                                      WORD logical_thread_id )
 {
@@ -1966,7 +2039,6 @@ static DWORD get_core_id_regs_arm64( struct smbios_wine_id_reg_value_arm64 *regs
         regs[regidx++] = (struct smbios_wine_id_reg_value_arm64){ 0x4000, value };
     }
 
-#ifdef HWCAP_CPUID
     if (!(getauxval(AT_HWCAP) & HWCAP_CPUID))
     {
         WARN( "Skipping ID register population as kernel is missing emulation support.\n" );
@@ -1999,9 +2071,6 @@ static DWORD get_core_id_regs_arm64( struct smbios_wine_id_reg_value_arm64 *regs
     READ_ID_REG( 0x5801 ); /* CTR_EL0 */
     /* Windows exposes SCTLR_EL1, ACTLR_EL1, TTBR0_EL1 and MAIR_EL1, but these are inaccessible under
      * linux so leave them unpopulated. */
-#else
-    WARN( "Skipping ID register population as HWCAP_CPUID isn't supported.\n" );
-#endif
 
 #undef READ_ID_REG
 #undef STR
diff --git a/dlls/ole32/stg_prop.c b/dlls/ole32/stg_prop.c
index 0fe8ecc18400..993f75d05dec 100644
--- a/dlls/ole32/stg_prop.c
+++ b/dlls/ole32/stg_prop.c
@@ -2342,6 +2342,13 @@ static HRESULT PropertyStorage_WritePropertyToStream(PropertyStorage_impl *This,
         bytesWritten = count + sizeof(DWORD);
         break;
     }
+    case VT_BOOL:
+    {
+        hr = IStream_Write(This->stm, &var->boolVal, sizeof(var->boolVal),
+         &count);
+        bytesWritten = count;
+        break;
+    }
     case VT_FILETIME:
     {
         FILETIME temp;
diff --git a/dlls/ole32/tests/stg_prop.c b/dlls/ole32/tests/stg_prop.c
index 1b08833adbe2..ea9cf735064b 100644
--- a/dlls/ole32/tests/stg_prop.c
+++ b/dlls/ole32/tests/stg_prop.c
@@ -361,6 +361,20 @@ static void testPropsHelper(IPropertySetStorage **propSetStorage)
     hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
     ok(hr == S_OK, "WriteMultiple failed: 0x%08lx\n", hr);
 
+    /* set a bool value */
+    spec.ulKind = PRSPEC_PROPID;
+    spec.propid = PID_FIRST_USABLE + 1;
+    var.vt = VT_BOOL;
+    var.boolVal = VARIANT_TRUE;
+    hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, 0);
+    ok(hr == S_OK, "WriteMultiple failed: 0x%08lx\n", hr);
+
+    spec.ulKind = PRSPEC_LPWSTR;
+    spec.lpwstr = (WCHAR*)L"TestBoolProp";
+    var.boolVal = VARIANT_FALSE;
+    hr = IPropertyStorage_WriteMultiple(propertyStorage, 1, &spec, &var, PID_FIRST_USABLE);
+    ok(hr == S_OK, "Write VT_BOOL (FALSE) by name failed: 0x%08lx\n", hr);
+
     hr = IPropertyStorage_Commit(propertyStorage, STGC_DEFAULT);
     ok(hr == S_OK, "Commit failed: 0x%08lx\n", hr);
 
@@ -403,6 +417,22 @@ static void testPropsHelper(IPropertySetStorage **propSetStorage)
      "Didn't get expected type or value for property (got type %d, value %ld)\n",
      var.vt, var.lVal);
 
+    PropVariantClear(&var);
+    spec.ulKind = PRSPEC_PROPID;
+    spec.propid = PID_FIRST_USABLE + 1;
+    hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
+    ok(hr == S_OK, "ReadMultiple failed: 0x%08lx\n", hr);
+    ok(var.vt == VT_BOOL && var.boolVal == VARIANT_TRUE,
+     "Didn't get expected type or value for property (got type %d, value %d)\n",
+     var.vt, var.boolVal);
+
+    PropVariantClear(&var);
+    spec.ulKind = PRSPEC_LPWSTR;
+    spec.lpwstr = (WCHAR*)L"TestBoolProp";
+    hr = IPropertyStorage_ReadMultiple(propertyStorage, 1, &spec, &var);
+    ok(hr == S_OK && var.vt == VT_BOOL && var.boolVal == VARIANT_FALSE,
+     "Read VT_BOOL (FALSE) failed\n");
+
     IPropertyStorage_Release(propertyStorage);
     if(propSetStorage) IPropertySetStorage_Release(*propSetStorage);
     IStorage_Release(storage);
diff --git a/dlls/opengl32/make_opengl b/dlls/opengl32/make_opengl
index 5ddd16af0daa..d94dd91e10bc 100755
--- a/dlls/opengl32/make_opengl
+++ b/dlls/opengl32/make_opengl
@@ -276,8 +276,9 @@ sub map_default_fbo($$)
 sub manual_wow64_wrapper($)
 {
     my $name = shift;
-    return 0 if $name =~ /^glBufferStorageExternal/;
-    return 0 if $name =~ /^glBufferStorageMem/;
+    return "glBufferAttachMemoryNV"        if $name =~ /^glBufferAttachMemory/;
+    return "glBufferData"                  if $name =~ /^glBufferData/;
+    return "glBufferStorageMemEXT"         if $name =~ /^glBufferStorageMem/;
     return "glBufferStorage"               if $name =~ /^glBufferStorage/;
     return "glDeleteBuffers"               if $name =~ /^glDeleteBuffers/;
     return "glFlushMappedBufferRange"      if $name =~ /^glFlushMappedBufferRange/;
@@ -288,29 +289,14 @@ sub manual_wow64_wrapper($)
     return "glMapBuffer"                   if $name =~ /^glMapBuffer/;
     return "glMapNamedBufferRange"         if $name =~ /^glMapNamedBufferRange/;
     return "glMapNamedBuffer"              if $name =~ /^glMapNamedBuffer/;
-    return 0 if $name =~ /^glNamedBufferStorageExternal/;
-    return 0 if $name =~ /^glNamedBufferStorageMem/;
+    return "glNamedBufferAttachMemoryNV"   if $name =~ /^glNamedBufferAttachMemory/;
+    return "glNamedBufferData"             if $name =~ /^glNamedBufferData/;
+    return "glNamedBufferStorageMemEXT"    if $name =~ /^glNamedBufferStorageMem/;
     return "glNamedBufferStorage"          if $name =~ /^glNamedBufferStorage/;
     return "glUnmapBuffer"                 if $name =~ /^glUnmapBuffer/;
     return "glUnmapNamedBuffer"            if $name =~ /^glUnmapNamedBuffer/;
 }
 
-my %wow64_invalidate_buffer =
-    (
-     "glBufferAttachMemoryNV" => 1,
-     "glBufferData" => 1,
-     "glBufferDataARB" => 1,
-     "glBufferStorage" => 1,
-     "glBufferStorageExternalEXT" => 1,
-     "glBufferStorageMemEXT" => 1,
-     "glNamedBufferAttachMemoryNV" => 1,
-     "glNamedBufferData" => 1,
-     "glNamedBufferDataEXT" => 1,
-     "glNamedBufferStorage" => 1,
-     "glNamedBufferStorageEXT" => 1,
-     "glNamedBufferStorageExternalEXT" => 1,
-     "glNamedBufferStorageMemEXT" => 1,
-    );
 my %pointer_array_count =
     (
      "glCompileShaderIncludeARB" => "count",
@@ -412,11 +398,9 @@ sub generate_unix_thunk($$$$)
     my $func_ret = get_func_ret( $func );
     my $wow64_wrap = $is_wow64 ? manual_wow64_wrapper( $name ) : 0;
     my $wrapper = $wow64_wrap || needs_wrapper( $name, $func );
-    my $need_lock = 0;
     my $need_funcs = !$wrapper || $name !~ /^wgl/;
     my $teb = $is_wow64 ? "teb" : "params->teb";
     my $ret_stat = "return STATUS_SUCCESS;";
-    my $invalidation = "";
     my $input_conv = "";
     my $output_conv = "";
     my $use_pbuffer = 0;
@@ -497,7 +481,6 @@ sub generate_unix_thunk($$$$)
             {
                 $call_args .= " ULongToPtr(params->$pname),";
                 $need_manual_thunk = 1 if $arg_type =~ /(\*.*\*|(sizei|int)ptr.*\*)/;
-                $need_lock = 1 if $arg_type =~ /GLsync/;
             }
         }
         $call_args .= " UlongToHandle( params->ret )," if $func_ret =~ /HPBUFFERARB|HGLRC|GLsync/;
@@ -526,16 +509,6 @@ sub generate_unix_thunk($$$$)
             $ret .= "}\n\n";
             return $ret;
         }
-        if (defined $wow64_invalidate_buffer{$name})
-        {
-            $need_lock = 1;
-            $need_funcs = 1;
-            $vars .= "    struct buffer *buffer;\n";
-            $invalidation .= $name =~ /NamedBuffer/
-                ? "    buffer = invalidate_buffer_name( $teb, params->buffer );\n"
-                : "    buffer = invalidate_buffer_target( $teb, params->target );\n";
-            $output_conv .= "    if (buffer) free_buffer( funcs, buffer );\n";
-        }
         $ret .= "    TEB *teb = get_teb64( params->teb );\n" if $wrapper || (!$use_dc && !$use_pbuffer && !$use_context);
     }
     else
@@ -599,7 +572,6 @@ sub generate_unix_thunk($$$$)
     }
     $ret .= "    resolve_default_fbo( $teb, TRUE );\n" if resolve_default_fbo( $name );
     $ret .= "    push_default_fbo( $teb );\n" if hide_default_fbo( $name );
-    $ret .= "    pthread_mutex_lock( &wgl_lock );\n$invalidation" if $need_lock;
     $ret .= "    $ret_expr";
     if ($wow64_wrap)
     {
@@ -616,7 +588,6 @@ sub generate_unix_thunk($$$$)
         $call_args =~ s/,$/ /;
         $ret .= "funcs->p_$name($call_args);\n";
     }
-    $ret .= "    pthread_mutex_unlock( &wgl_lock );\n" if $need_lock;
     $ret .= "    set_current_fbo( $teb, params->target, params->framebuffer );\n" if $name =~ /glBindFramebuffer/;
     $ret .= "    pop_default_fbo( $teb );\n" if hide_default_fbo( $name );
     if (defined $state_attrib_funcs{$name})
@@ -1106,6 +1077,7 @@ my %unsupported_extgroups =
    );
 my %unsupported_extensions =
    (
+    "GL_EXT_external_buffer" => 1,
     "WGL_AMD_gpu_association" => 1,
     "WGL_ARB_buffer_region" => 1,
     "WGL_EXT_display_color_table" => 1,
diff --git a/dlls/opengl32/tests/opengl.c b/dlls/opengl32/tests/opengl.c
index 89d6d072f480..e82ca4028c40 100644
--- a/dlls/opengl32/tests/opengl.c
+++ b/dlls/opengl32/tests/opengl.c
@@ -3218,120 +3218,280 @@ static void test_dc(HWND hwnd, HDC hdc)
     }
 }
 
-/* Nvidia converts win32 error codes to (0xc007 << 16) | win32_error_code */
-#define NVIDIA_HRESULT_FROM_WIN32(x) (HRESULT_FROM_WIN32(x) | 0x40000000)
-static void test_opengl3(HDC hdc)
+static void test_wglCreateContextAttribsARB( HDC hdc )
 {
-    /* Try to create a context compatible with OpenGL 1.x; 1.0-2.1 is allowed */
-    {
-        HGLRC gl3Ctx;
-        int attribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, 1, 0};
+    static const int gl30_fwd_attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 0, WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 0 };
+    static const int gl32_fwd_attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 2, WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, 0 };
+    static const int gl32_comp_attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 2, WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB, 0 };
+
+    static const int gl10_attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 1, 0 };
+    static const int gl21_attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 2, WGL_CONTEXT_MINOR_VERSION_ARB, 1, 0 };
+    static const int gl30_attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 0, 0 };
+    static const int gl31_attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 1, 0 };
+    static const int gl32_attribs[] = { WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 2, 0 };
+
+    GLint num, major, minor, expect_major, expect_minor, flags, profile;
+    const GLubyte *extension;
+    HGLRC ctx, tmp;
 
-        gl3Ctx = ext.wglCreateContextAttribsARB( hdc, 0, attribs );
-        ok( gl3Ctx != 0, "ext.wglCreateContextAttribsARB for a 1.x context failed!\n" );
-        wglDeleteContext(gl3Ctx);
-    }
 
     /* Try to pass an invalid HDC */
-    {
-        HGLRC gl3Ctx;
-        DWORD error;
-        SetLastError(0xdeadbeef);
-        gl3Ctx = ext.wglCreateContextAttribsARB( (HDC)0xdeadbeef, 0, 0 );
-        ok( gl3Ctx == 0, "ext.wglCreateContextAttribsARB using an invalid HDC passed\n" );
-        error = GetLastError();
-        ok(error == ERROR_DC_NOT_FOUND || error == ERROR_INVALID_HANDLE ||
-           broken(error == ERROR_DS_GENERIC_ERROR) ||
-           broken(error == NVIDIA_HRESULT_FROM_WIN32(ERROR_INVALID_DATA)), /* Nvidia Vista + Win7 */
-           "Expected ERROR_DC_NOT_FOUND, got error=%lx\n", error);
-        wglDeleteContext(gl3Ctx);
-    }
+    SetLastError( 0xdeadbeef );
+    ctx = ext.wglCreateContextAttribsARB( (HDC)0xdeadbeef, 0, 0 );
+    ok_ptr( ctx, ==, NULL );
+    ok( GetLastError() == ERROR_DC_NOT_FOUND || GetLastError() == ERROR_INVALID_HANDLE ||
+        broken( GetLastError() == ERROR_DS_GENERIC_ERROR ) || broken( LOWORD( GetLastError() ) == ERROR_INVALID_DATA ), /* Nvidia Vista + Win7 */
+        "got error %ld\n", GetLastError() );
 
     /* Try to pass an invalid shareList */
-    {
-        HGLRC gl3Ctx;
-        DWORD error;
-        SetLastError(0xdeadbeef);
-        gl3Ctx = ext.wglCreateContextAttribsARB( hdc, (HGLRC)0xdeadbeef, 0 );
-        ok( gl3Ctx == 0, "ext.wglCreateContextAttribsARB using an invalid shareList passed\n" );
-        error = GetLastError();
-        /* The Nvidia implementation seems to return hresults instead of win32 error codes */
-        ok(error == ERROR_INVALID_OPERATION || error == ERROR_INVALID_DATA ||
-           error == NVIDIA_HRESULT_FROM_WIN32(ERROR_INVALID_OPERATION), "Expected ERROR_INVALID_OPERATION, got error=%lx\n", error);
-        wglDeleteContext(gl3Ctx);
-    }
+    SetLastError( 0xdeadbeef );
+    ctx = ext.wglCreateContextAttribsARB( hdc, (HGLRC)0xdeadbeef, 0 );
+    ok_ptr( ctx, ==, NULL );
+    ok( LOWORD( GetLastError() ) == ERROR_INVALID_OPERATION || GetLastError() == ERROR_INVALID_DATA,
+        "got %ld\n", GetLastError() );
 
-    /* Try to create an OpenGL 3.0 context */
-    {
-        int attribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 0, 0};
-        HGLRC gl3Ctx = ext.wglCreateContextAttribsARB( hdc, 0, attribs );
-        const GLubyte *extension;
-        GLint num;
 
-        if(gl3Ctx == NULL)
-        {
-            skip("Skipping the rest of the WGL_ARB_create_context test due to lack of OpenGL 3.0\n");
-            return;
-        }
+    ctx = ext.wglCreateContextAttribsARB( hdc, NULL, NULL );
+    ok_ptr( ctx, !=, NULL );
+    ok_ret( TRUE, wglMakeCurrent( hdc, ctx ) );
 
-        wglMakeCurrent(hdc, gl3Ctx);
+    glGetIntegerv( GL_MAJOR_VERSION, &expect_major );
+    glGetIntegerv( GL_MINOR_VERSION, &expect_minor );
+    ok_ret( 0, glGetError() );
+    ok_u4( expect_major, >=, 3 );
+    if (expect_major == 3) ok_u4( expect_minor, >, 1 );
 
-        glGetIntegerv(GL_NUM_EXTENSIONS, &num);
-        ok(num > 0, "got %u\n", num);
-        check_gl_error(0);
-        extension = ext.glGetStringi( GL_EXTENSIONS, 0 );
-        ok( !!extension, "got %p\n", extension );
-        check_gl_error(0);
-        extension = ext.glGetStringi( GL_EXTENSIONS, num );
-        ok( !extension, "got %p\n", extension );
-        check_gl_error(GL_INVALID_VALUE);
+    glGetIntegerv( GL_CONTEXT_PROFILE_MASK, &profile );
+    glGetIntegerv( GL_CONTEXT_FLAGS, &flags );
+    ok_ret( 0, glGetError() );
+    ok_x4( flags, ==, 0 );
+    ok( profile == GL_CONTEXT_COMPATIBILITY_PROFILE_BIT || profile == 0 /* NVIDIA */, "got profile %#x\n", profile );
 
-        wglDeleteContext(gl3Ctx);
-    }
+    glGetIntegerv( GL_NUM_EXTENSIONS, &num );
+    ok_u4( num, >, 0 );
+    ok_ret( 0, glGetError() );
 
-    /* Test matching an OpenGL 3.0 context with an older one, OpenGL 3.0 should allow it until the new object model is introduced in a future revision */
-    {
-        HGLRC glCtx = wglCreateContext(hdc);
+    if (!ext.glGetStringi) extension = NULL;
+    else extension = ext.glGetStringi( GL_EXTENSIONS, 0 );
+    ok_ptr( extension, !=, NULL );
+    ok_ret( 0, glGetError() );
+
+    if (!ext.glGetStringi) extension = NULL;
+    else extension = ext.glGetStringi( GL_EXTENSIONS, num );
+    ok_ptr( extension, ==, NULL );
+    ok_ret( GL_INVALID_VALUE, glGetError() );
+
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
 
-        int attribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 0, 0};
-        int attribs_future[] = {WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 0, 0};
 
-        HGLRC gl3Ctx = ext.wglCreateContextAttribsARB( hdc, glCtx, attribs );
-        ok(gl3Ctx != NULL, "Sharing of a display list between OpenGL 3.0 and OpenGL 1.x/2.x failed!\n");
-        if(gl3Ctx)
-            wglDeleteContext(gl3Ctx);
+    /* wglCreateContext is the same as wglCreateContextAttribsARB without attribs */
+    ctx = wglCreateContext( hdc );
+    ok_ptr( ctx, !=, NULL );
+    ok_ret( TRUE, wglMakeCurrent( hdc, ctx ) );
 
-        gl3Ctx = ext.wglCreateContextAttribsARB( hdc, glCtx, attribs_future );
-        ok(gl3Ctx != NULL, "Sharing of a display list between a forward compatible OpenGL 3.0 context and OpenGL 1.x/2.x failed!\n");
-        if(gl3Ctx)
-            wglDeleteContext(gl3Ctx);
+    glGetIntegerv( GL_MAJOR_VERSION, &major );
+    glGetIntegerv( GL_MINOR_VERSION, &minor );
+    ok_ret( 0, glGetError() );
+    ok_u4( major, ==, expect_major );
+    ok_u4( minor, ==, expect_minor );
+
+    glGetIntegerv( GL_CONTEXT_PROFILE_MASK, &profile );
+    glGetIntegerv( GL_CONTEXT_FLAGS, &flags );
+    ok_ret( 0, glGetError() );
+    ok_x4( flags, ==, 0 );
+    ok( profile == GL_CONTEXT_COMPATIBILITY_PROFILE_BIT || profile == 0 /* NVIDIA */, "got profile %#x\n", profile );
+
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
+
+
+    /* requesting non-forward version < 3.0 is ignored */
+    ctx = ext.wglCreateContextAttribsARB( hdc, NULL, gl10_attribs );
+    ok_ptr( ctx, !=, NULL );
+    ok_ret( TRUE, wglMakeCurrent( hdc, ctx ) );
+
+    glGetIntegerv( GL_MAJOR_VERSION, &major );
+    glGetIntegerv( GL_MINOR_VERSION, &minor );
+    ok_ret( 0, glGetError() );
+    ok_u4( major, ==, expect_major );
+    ok_u4( minor, ==, expect_minor );
+
+    glGetIntegerv( GL_CONTEXT_PROFILE_MASK, &profile );
+    glGetIntegerv( GL_CONTEXT_FLAGS, &flags );
+    ok_ret( 0, glGetError() );
+    ok_x4( flags, ==, 0 );
+    ok_x4( profile, ==, GL_CONTEXT_COMPATIBILITY_PROFILE_BIT );
+
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
+
+    ctx = ext.wglCreateContextAttribsARB( hdc, NULL, gl21_attribs );
+    ok_ptr( ctx, !=, NULL );
+    ok_ret( TRUE, wglMakeCurrent( hdc, ctx ) );
+
+    glGetIntegerv( GL_MAJOR_VERSION, &major );
+    glGetIntegerv( GL_MINOR_VERSION, &minor );
+    ok_ret( 0, glGetError() );
+    ok_u4( major, ==, expect_major );
+    ok_u4( minor, ==, expect_minor );
+
+    glGetIntegerv( GL_CONTEXT_PROFILE_MASK, &profile );
+    glGetIntegerv( GL_CONTEXT_FLAGS, &flags );
+    ok_ret( 0, glGetError() );
+    ok_x4( flags, ==, 0 );
+    ok_x4( profile, ==, GL_CONTEXT_COMPATIBILITY_PROFILE_BIT );
+
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
+
+
+    /* AMD allows 3.0 context creation without forward-compatible bit */
+    ctx = ext.wglCreateContextAttribsARB( hdc, NULL, gl30_attribs );
+    ok_ptr( ctx, !=, NULL );
+    ok_ret( TRUE, wglMakeCurrent( hdc, ctx ) );
+
+    glGetIntegerv( GL_MAJOR_VERSION, &major );
+    glGetIntegerv( GL_MINOR_VERSION, &minor );
+    ok_ret( 0, glGetError() );
+    ok( major == expect_major || broken( major == 3 ) /* AMD */, "got major %d\n", major );
+    ok( minor == expect_minor || broken( minor == 0 ) /* AMD */, "got minor %d\n", minor );
+
+    glGetIntegerv( GL_CONTEXT_PROFILE_MASK, &profile );
+    glGetIntegerv( GL_CONTEXT_FLAGS, &flags );
+    ok_ret( 0, glGetError() );
+    ok_x4( flags, ==, 0 );
+    ok_x4( profile, ==, GL_CONTEXT_COMPATIBILITY_PROFILE_BIT );
+
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
+
+
+    /* requesting forward-compatible version 3.0 is respected */
+    ctx = ext.wglCreateContextAttribsARB( hdc, NULL, gl30_fwd_attribs );
+    ok_ptr( ctx, !=, NULL );
+    ok_ret( TRUE, wglMakeCurrent( hdc, ctx ) );
+
+    glGetIntegerv( GL_MAJOR_VERSION, &major );
+    glGetIntegerv( GL_MINOR_VERSION, &minor );
+    ok_ret( 0, glGetError() );
+    todo_wine_if( expect_major != 3 ) ok_u4( major, ==, 3 );
+    todo_wine_if( expect_minor != 0 ) ok_u4( minor, ==, 0 );
+
+    glGetIntegerv( GL_CONTEXT_FLAGS, &flags );
+    ok_ret( 0, glGetError() );
+    ok_x4( flags, ==, GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT );
+
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
+
+
+    /* requesting version >= 3.1 is respected */
+    ctx = ext.wglCreateContextAttribsARB( hdc, NULL, gl31_attribs );
+    ok_ptr( ctx, !=, NULL );
+    ok_ret( TRUE, wglMakeCurrent( hdc, ctx ) );
+
+    glGetIntegerv( GL_MAJOR_VERSION, &major );
+    glGetIntegerv( GL_MINOR_VERSION, &minor );
+    ok_ret( 0, glGetError() );
+    todo_wine_if( expect_major != 3 ) ok_u4( major, ==, 3 );
+    todo_wine_if( expect_minor != 1 ) ok_u4( minor, ==, 1 );
+
+    glGetIntegerv( GL_CONTEXT_FLAGS, &flags );
+    ok_ret( 0, glGetError() );
+    ok_x4( flags, ==, 0 );
+
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
+
+    /* requesting version >= 3.2 is core by default */
+    ctx = ext.wglCreateContextAttribsARB( hdc, NULL, gl32_attribs );
+    ok_ptr( ctx, !=, NULL );
+    ok_ret( TRUE, wglMakeCurrent( hdc, ctx ) );
+
+    glGetIntegerv( GL_MAJOR_VERSION, &major );
+    glGetIntegerv( GL_MINOR_VERSION, &minor );
+    ok_ret( 0, glGetError() );
+    todo_wine_if( expect_major != 3 ) ok_u4( major, ==, 3 );
+    todo_wine_if( expect_minor != 2 ) ok_u4( minor, ==, 2 );
+
+    glGetIntegerv( GL_CONTEXT_PROFILE_MASK, &profile );
+    glGetIntegerv( GL_CONTEXT_FLAGS, &flags );
+    ok_ret( 0, glGetError() );
+    ok_x4( flags, ==, 0 );
+    ok_x4( profile, ==, GL_CONTEXT_CORE_PROFILE_BIT );
+
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
+
+
+    ctx = ext.wglCreateContextAttribsARB( hdc, NULL, gl32_fwd_attribs );
+    ok_ptr( ctx, !=, NULL );
+    ok_ret( TRUE, wglMakeCurrent( hdc, ctx ) );
+
+    glGetIntegerv( GL_MAJOR_VERSION, &major );
+    glGetIntegerv( GL_MINOR_VERSION, &minor );
+    ok_ret( 0, glGetError() );
+    todo_wine_if( expect_minor != 3 ) ok_u4( major, ==, 3 );
+    todo_wine_if( expect_minor != 2 ) ok_u4( minor, ==, 2 );
+
+    glGetIntegerv( GL_CONTEXT_PROFILE_MASK, &profile );
+    glGetIntegerv( GL_CONTEXT_FLAGS, &flags );
+    ok_ret( 0, glGetError() );
+    ok_x4( flags, ==, GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT );
+    ok_x4( profile, ==, GL_CONTEXT_CORE_PROFILE_BIT );
+
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
+
+
+    ctx = ext.wglCreateContextAttribsARB( hdc, NULL, gl32_comp_attribs );
+    ok_ptr( ctx, !=, NULL );
+    ok_ret( TRUE, wglMakeCurrent( hdc, ctx ) );
+
+    glGetIntegerv( GL_MAJOR_VERSION, &major );
+    glGetIntegerv( GL_MINOR_VERSION, &minor );
+    ok_ret( 0, glGetError() );
+    ok( major == 3 /* AMD */ || major == expect_major /* NVIDIA */, "got major %u\n", major );
+    ok( minor == 2 /* AMD */ || minor == expect_minor /* NVIDIA */, "got minor %u\n", minor );
+
+    glGetIntegerv( GL_CONTEXT_PROFILE_MASK, &profile );
+    glGetIntegerv( GL_CONTEXT_FLAGS, &flags );
+    ok_ret( 0, glGetError() );
+    ok_x4( flags, ==, 0 );
+    ok_x4( profile, ==, GL_CONTEXT_COMPATIBILITY_PROFILE_BIT );
+
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
+
+
+    /* sharing is always possible regardless of core / compat / version */
+    ctx = wglCreateContext( hdc );
+    ok_ptr( ctx, !=, NULL );
+
+    tmp = ext.wglCreateContextAttribsARB( hdc, ctx, gl21_attribs );
+    ok_ptr( tmp, !=, NULL );
+    ok_ret( TRUE, wglDeleteContext( tmp ) );
+    tmp = ext.wglCreateContextAttribsARB( hdc, ctx, gl30_attribs );
+    ok_ptr( tmp, !=, NULL );
+    ok_ret( TRUE, wglDeleteContext( tmp ) );
+    tmp = ext.wglCreateContextAttribsARB( hdc, ctx, gl30_fwd_attribs );
+    ok_ptr( tmp, !=, NULL );
+    ok_ret( TRUE, wglDeleteContext( tmp ) );
+    tmp = ext.wglCreateContextAttribsARB( hdc, ctx, gl32_comp_attribs );
+    ok_ptr( tmp, !=, NULL );
+    ok_ret( TRUE, wglDeleteContext( tmp ) );
+    tmp = ext.wglCreateContextAttribsARB( hdc, ctx, gl31_attribs );
+    ok_ptr( tmp, !=, NULL );
+    ok_ret( TRUE, wglDeleteContext( tmp ) );
+    tmp = ext.wglCreateContextAttribsARB( hdc, ctx, gl32_attribs );
+    ok_ptr( tmp, !=, NULL );
+    ok_ret( TRUE, wglDeleteContext( tmp ) );
+
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
 
-        if(glCtx)
-            wglDeleteContext(glCtx);
-    }
 
     /* Try to create an OpenGL 3.0 context and test windowless rendering */
-    {
-        HGLRC gl3Ctx;
-        int attribs[] = {WGL_CONTEXT_MAJOR_VERSION_ARB, 3, WGL_CONTEXT_MINOR_VERSION_ARB, 0, 0};
-        BOOL res;
-
-        gl3Ctx = ext.wglCreateContextAttribsARB( hdc, 0, attribs );
-        ok( gl3Ctx != 0, "ext.wglCreateContextAttribsARB for a 3.0 context failed!\n" );
-
-        /* OpenGL 3.0 allows offscreen rendering WITHOUT a drawable
-         * Neither AMD or Nvidia support it at this point. The WGL_ARB_create_context specs also say that
-         * it is hard because drivers use the HDC to enter the display driver and it sounds like they don't
-         * expect drivers to ever offer it.
-         */
-        res = wglMakeCurrent(0, gl3Ctx);
-        ok(res == FALSE, "Wow, OpenGL 3.0 windowless rendering passed while it was expected not to!\n");
-        if(res)
-            wglMakeCurrent(0, 0);
-
-        if(gl3Ctx)
-            wglDeleteContext(gl3Ctx);
-    }
+    ctx = ext.wglCreateContextAttribsARB( hdc, 0, gl32_attribs );
+    ok_ptr( ctx, !=, NULL );
+
+    /* OpenGL 3.0 allows offscreen rendering WITHOUT a drawable
+     * Neither AMD or Nvidia support it at this point. The WGL_ARB_create_context specs also say that
+     * it is hard because drivers use the HDC to enter the display driver and it sounds like they don't
+     * expect drivers to ever offer it.
+     */
+    ok_ret( FALSE, wglMakeCurrent( NULL, ctx ) );
+    ok_ret( TRUE, wglDeleteContext( ctx ) );
 }
 
 static void test_minimized(void)
@@ -5243,7 +5403,7 @@ START_TEST(opengl)
 
     if (strstr( wgl_extensions, "WGL_ARB_create_context" ))
     {
-        test_opengl3( hdc );
+        test_wglCreateContextAttribsARB( hdc );
         test_object_creation( hdc );
     }
 
diff --git a/dlls/opengl32/thunks.c b/dlls/opengl32/thunks.c
index 7d4efe25ac79..471aae69a938 100644
--- a/dlls/opengl32/thunks.c
+++ b/dlls/opengl32/thunks.c
@@ -4022,14 +4022,6 @@ static void WINAPI glBufferStorage( GLenum target, GLsizeiptr size, const void *
     if ((status = UNIX_CALL( glBufferStorage, &args ))) WARN( "glBufferStorage returned %#lx\n", status );
 }
 
-static void WINAPI glBufferStorageExternalEXT( GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags )
-{
-    struct glBufferStorageExternalEXT_params args = { .teb = NtCurrentTeb(), .target = target, .offset = offset, .size = size, .clientBuffer = clientBuffer, .flags = flags };
-    NTSTATUS status;
-    TRACE( "target %d, offset %Id, size %Id, clientBuffer %p, flags %d\n", target, offset, size, clientBuffer, flags );
-    if ((status = UNIX_CALL( glBufferStorageExternalEXT, &args ))) WARN( "glBufferStorageExternalEXT returned %#lx\n", status );
-}
-
 static void WINAPI glBufferStorageMemEXT( GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset )
 {
     struct glBufferStorageMemEXT_params args = { .teb = NtCurrentTeb(), .target = target, .size = size, .offset = offset };
@@ -15220,15 +15212,6 @@ static void WINAPI glNamedBufferStorageEXT( GLuint buffer, GLsizeiptr size, cons
     if ((status = UNIX_CALL( glNamedBufferStorageEXT, &args ))) WARN( "glNamedBufferStorageEXT returned %#lx\n", status );
 }
 
-static void WINAPI glNamedBufferStorageExternalEXT( GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags )
-{
-    struct glNamedBufferStorageExternalEXT_params args = { .teb = NtCurrentTeb(), .offset = offset, .size = size, .clientBuffer = clientBuffer, .flags = flags };
-    NTSTATUS status;
-    TRACE( "buffer %d, offset %Id, size %Id, clientBuffer %p, flags %d\n", buffer, offset, size, clientBuffer, flags );
-    args.buffer = *map_context_objects( OBJ_TYPE_BUFFER, 1, &buffer );
-    if ((status = UNIX_CALL( glNamedBufferStorageExternalEXT, &args ))) WARN( "glNamedBufferStorageExternalEXT returned %#lx\n", status );
-}
-
 static void WINAPI glNamedBufferStorageMemEXT( GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset )
 {
     struct glNamedBufferStorageMemEXT_params args = { .teb = NtCurrentTeb(), .size = size, .offset = offset };
@@ -26248,7 +26231,6 @@ const void *extension_procs[] =
     glBufferParameteriAPPLE,
     glBufferRegionEnabled,
     glBufferStorage,
-    glBufferStorageExternalEXT,
     glBufferStorageMemEXT,
     glBufferSubData,
     glBufferSubDataARB,
@@ -27569,7 +27551,6 @@ const void *extension_procs[] =
     glNamedBufferPageCommitmentMemNV,
     glNamedBufferStorage,
     glNamedBufferStorageEXT,
-    glNamedBufferStorageExternalEXT,
     glNamedBufferStorageMemEXT,
     glNamedBufferSubData,
     glNamedBufferSubDataEXT,
@@ -29009,7 +28990,6 @@ const struct registry_entry extension_registry[] =
     { "glBufferParameteriAPPLE", glBufferParameteriAPPLE, 0, 0, { GL_APPLE_flush_buffer_range, GL_EXTENSION_COUNT }},
     { "glBufferRegionEnabled", glBufferRegionEnabled, 0, 0, { GL_KTX_buffer_region, GL_EXTENSION_COUNT }},
     { "glBufferStorage", glBufferStorage, 4, 4, { GL_ARB_buffer_storage, GL_EXTENSION_COUNT }},
-    { "glBufferStorageExternalEXT", glBufferStorageExternalEXT, 0, 0, { GL_EXT_external_buffer, GL_EXTENSION_COUNT }},
     { "glBufferStorageMemEXT", glBufferStorageMemEXT, 0, 0, { GL_EXT_memory_object, GL_EXTENSION_COUNT }},
     { "glBufferSubData", glBufferSubData, 1, 5, { GL_EXTENSION_COUNT }},
     { "glBufferSubDataARB", glBufferSubDataARB, 0, 0, { GL_ARB_vertex_buffer_object, GL_EXTENSION_COUNT }},
@@ -30330,7 +30310,6 @@ const struct registry_entry extension_registry[] =
     { "glNamedBufferPageCommitmentMemNV", glNamedBufferPageCommitmentMemNV, 0, 0, { GL_NV_memory_object_sparse, GL_EXTENSION_COUNT }},
     { "glNamedBufferStorage", glNamedBufferStorage, 4, 5, { GL_ARB_direct_state_access, GL_EXTENSION_COUNT }},
     { "glNamedBufferStorageEXT", glNamedBufferStorageEXT, 0, 0, { GL_EXT_direct_state_access, GL_EXTENSION_COUNT }},
-    { "glNamedBufferStorageExternalEXT", glNamedBufferStorageExternalEXT, 0, 0, { GL_EXT_external_buffer, GL_EXTENSION_COUNT }},
     { "glNamedBufferStorageMemEXT", glNamedBufferStorageMemEXT, 0, 0, { GL_EXT_memory_object, GL_EXTENSION_COUNT }},
     { "glNamedBufferSubData", glNamedBufferSubData, 4, 5, { GL_ARB_direct_state_access, GL_EXTENSION_COUNT }},
     { "glNamedBufferSubDataEXT", glNamedBufferSubDataEXT, 0, 0, { GL_EXT_direct_state_access, GL_EXTENSION_COUNT }},
diff --git a/dlls/opengl32/unix_private.h b/dlls/opengl32/unix_private.h
index 8f05f6218e73..10722acf7ecf 100644
--- a/dlls/opengl32/unix_private.h
+++ b/dlls/opengl32/unix_private.h
@@ -81,15 +81,10 @@ static inline TEB *get_teb64( ULONG teb32 )
     return (TEB *)((char *)teb32_ptr + teb32_ptr->WowTebOffset);
 }
 
-extern struct buffer *invalidate_buffer_name( TEB *teb, GLuint name );
-extern struct buffer *invalidate_buffer_target( TEB *teb, GLenum target );
-extern void free_buffer( const struct opengl_funcs *funcs, struct buffer *buffer );
 extern NTSTATUS return_wow64_string( const void *str, PTR32 *wow64_str );
 
 #endif
 
-extern pthread_mutex_t wgl_lock;
-
 extern NTSTATUS process_attach( void *args );
 extern NTSTATUS thread_attach( void *args );
 extern NTSTATUS process_detach( void *args );
diff --git a/dlls/opengl32/unix_thunks.c b/dlls/opengl32/unix_thunks.c
index 1d3f8a8dbd3d..ee35199ffc05 100644
--- a/dlls/opengl32/unix_thunks.c
+++ b/dlls/opengl32/unix_thunks.c
@@ -4818,16 +4818,6 @@ static NTSTATUS ext_glBufferStorage( void *args )
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS ext_glBufferStorageExternalEXT( void *args )
-{
-    struct glBufferStorageExternalEXT_params *params = args;
-    const struct opengl_funcs *funcs = params->teb->glTable;
-    if (!funcs->p_glBufferStorageExternalEXT) return STATUS_NOT_IMPLEMENTED;
-    funcs->p_glBufferStorageExternalEXT( params->target, params->offset, params->size, params->clientBuffer, params->flags );
-    set_context_attribute( params->teb, -1 /* unsupported */, NULL, 0 );
-    return STATUS_SUCCESS;
-}
-
 static NTSTATUS ext_glBufferStorageMemEXT( void *args )
 {
     struct glBufferStorageMemEXT_params *params = args;
@@ -17627,16 +17617,6 @@ static NTSTATUS ext_glNamedBufferStorageEXT( void *args )
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS ext_glNamedBufferStorageExternalEXT( void *args )
-{
-    struct glNamedBufferStorageExternalEXT_params *params = args;
-    const struct opengl_funcs *funcs = params->teb->glTable;
-    if (!funcs->p_glNamedBufferStorageExternalEXT) return STATUS_NOT_IMPLEMENTED;
-    funcs->p_glNamedBufferStorageExternalEXT( params->buffer, params->offset, params->size, params->clientBuffer, params->flags );
-    set_context_attribute( params->teb, -1 /* unsupported */, NULL, 0 );
-    return STATUS_SUCCESS;
-}
-
 static NTSTATUS ext_glNamedBufferStorageMemEXT( void *args )
 {
     struct glNamedBufferStorageMemEXT_params *params = args;
@@ -31006,7 +30986,6 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     ext_glBufferParameteriAPPLE,
     ext_glBufferRegionEnabled,
     ext_glBufferStorage,
-    ext_glBufferStorageExternalEXT,
     ext_glBufferStorageMemEXT,
     ext_glBufferSubData,
     ext_glBufferSubDataARB,
@@ -32327,7 +32306,6 @@ const unixlib_entry_t __wine_unix_call_funcs[] =
     ext_glNamedBufferPageCommitmentMemNV,
     ext_glNamedBufferStorage,
     ext_glNamedBufferStorageEXT,
-    ext_glNamedBufferStorageExternalEXT,
     ext_glNamedBufferStorageMemEXT,
     ext_glNamedBufferSubData,
     ext_glNamedBufferSubDataEXT,
@@ -41498,15 +41476,10 @@ static NTSTATUS wow64_ext_glBufferAttachMemoryNV( void *args )
         GLuint64 offset;
     } *params = args;
     TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
     const struct opengl_funcs *funcs = teb->glTable;
     if (!funcs->p_glBufferAttachMemoryNV) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_target( teb, params->target );
-    funcs->p_glBufferAttachMemoryNV( params->target, params->memory, params->offset );
-    pthread_mutex_unlock( &wgl_lock );
+    wow64_glBufferAttachMemoryNV( teb, params->target, params->memory, params->offset, funcs->p_glBufferAttachMemoryNV );
     set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
     return STATUS_SUCCESS;
 }
 
@@ -41521,15 +41494,10 @@ static NTSTATUS wow64_ext_glBufferData( void *args )
         GLenum usage;
     } *params = args;
     TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
     const struct opengl_funcs *funcs = teb->glTable;
     if (!funcs->p_glBufferData) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_target( teb, params->target );
-    funcs->p_glBufferData( params->target, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->usage );
-    pthread_mutex_unlock( &wgl_lock );
+    wow64_glBufferData( teb, params->target, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->usage, funcs->p_glBufferData );
     set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
     return STATUS_SUCCESS;
 }
 
@@ -41544,15 +41512,10 @@ static NTSTATUS wow64_ext_glBufferDataARB( void *args )
         GLenum usage;
     } *params = args;
     TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
     const struct opengl_funcs *funcs = teb->glTable;
     if (!funcs->p_glBufferDataARB) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_target( teb, params->target );
-    funcs->p_glBufferDataARB( params->target, (GLsizeiptrARB)ULongToPtr(params->size), ULongToPtr(params->data), params->usage );
-    pthread_mutex_unlock( &wgl_lock );
+    wow64_glBufferData( teb, params->target, (GLsizeiptrARB)ULongToPtr(params->size), ULongToPtr(params->data), params->usage, funcs->p_glBufferDataARB );
     set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
     return STATUS_SUCCESS;
 }
 
@@ -41637,39 +41600,10 @@ static NTSTATUS wow64_ext_glBufferStorage( void *args )
         GLbitfield flags;
     } *params = args;
     TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
     const struct opengl_funcs *funcs = teb->glTable;
     if (!funcs->p_glBufferStorage) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_target( teb, params->target );
     wow64_glBufferStorage( teb, params->target, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->flags, funcs->p_glBufferStorage );
-    pthread_mutex_unlock( &wgl_lock );
-    set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS wow64_ext_glBufferStorageExternalEXT( void *args )
-{
-    struct
-    {
-        PTR32 teb;
-        GLenum target;
-        PTR32 offset;
-        PTR32 size;
-        PTR32 clientBuffer;
-        GLbitfield flags;
-    } *params = args;
-    TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
-    const struct opengl_funcs *funcs = teb->glTable;
-    if (!funcs->p_glBufferStorageExternalEXT) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_target( teb, params->target );
-    funcs->p_glBufferStorageExternalEXT( params->target, (GLintptr)ULongToPtr(params->offset), (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->clientBuffer), params->flags );
-    pthread_mutex_unlock( &wgl_lock );
     set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
     return STATUS_SUCCESS;
 }
 
@@ -41684,15 +41618,10 @@ static NTSTATUS wow64_ext_glBufferStorageMemEXT( void *args )
         GLuint64 offset;
     } *params = args;
     TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
     const struct opengl_funcs *funcs = teb->glTable;
     if (!funcs->p_glBufferStorageMemEXT) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_target( teb, params->target );
-    funcs->p_glBufferStorageMemEXT( params->target, (GLsizeiptr)ULongToPtr(params->size), params->memory, params->offset );
-    pthread_mutex_unlock( &wgl_lock );
+    wow64_glBufferStorageMemEXT( teb, params->target, (GLsizeiptr)ULongToPtr(params->size), params->memory, params->offset, funcs->p_glBufferStorageMemEXT );
     set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
     return STATUS_SUCCESS;
 }
 
@@ -64193,15 +64122,10 @@ static NTSTATUS wow64_ext_glNamedBufferAttachMemoryNV( void *args )
         GLuint64 offset;
     } *params = args;
     TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
     const struct opengl_funcs *funcs = teb->glTable;
     if (!funcs->p_glNamedBufferAttachMemoryNV) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_name( teb, params->buffer );
-    funcs->p_glNamedBufferAttachMemoryNV( params->buffer, params->memory, params->offset );
-    pthread_mutex_unlock( &wgl_lock );
+    wow64_glNamedBufferAttachMemoryNV( teb, params->buffer, params->memory, params->offset, funcs->p_glNamedBufferAttachMemoryNV );
     set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
     return STATUS_SUCCESS;
 }
 
@@ -64216,15 +64140,10 @@ static NTSTATUS wow64_ext_glNamedBufferData( void *args )
         GLenum usage;
     } *params = args;
     TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
     const struct opengl_funcs *funcs = teb->glTable;
     if (!funcs->p_glNamedBufferData) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_name( teb, params->buffer );
-    funcs->p_glNamedBufferData( params->buffer, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->usage );
-    pthread_mutex_unlock( &wgl_lock );
+    wow64_glNamedBufferData( teb, params->buffer, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->usage, funcs->p_glNamedBufferData );
     set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
     return STATUS_SUCCESS;
 }
 
@@ -64239,15 +64158,10 @@ static NTSTATUS wow64_ext_glNamedBufferDataEXT( void *args )
         GLenum usage;
     } *params = args;
     TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
     const struct opengl_funcs *funcs = teb->glTable;
     if (!funcs->p_glNamedBufferDataEXT) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_name( teb, params->buffer );
-    funcs->p_glNamedBufferDataEXT( params->buffer, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->usage );
-    pthread_mutex_unlock( &wgl_lock );
+    wow64_glNamedBufferData( teb, params->buffer, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->usage, funcs->p_glNamedBufferDataEXT );
     set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
     return STATUS_SUCCESS;
 }
 
@@ -64318,15 +64232,10 @@ static NTSTATUS wow64_ext_glNamedBufferStorage( void *args )
         GLbitfield flags;
     } *params = args;
     TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
     const struct opengl_funcs *funcs = teb->glTable;
     if (!funcs->p_glNamedBufferStorage) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_name( teb, params->buffer );
     wow64_glNamedBufferStorage( teb, params->buffer, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->flags, funcs->p_glNamedBufferStorage );
-    pthread_mutex_unlock( &wgl_lock );
     set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
     return STATUS_SUCCESS;
 }
 
@@ -64341,39 +64250,10 @@ static NTSTATUS wow64_ext_glNamedBufferStorageEXT( void *args )
         GLbitfield flags;
     } *params = args;
     TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
     const struct opengl_funcs *funcs = teb->glTable;
     if (!funcs->p_glNamedBufferStorageEXT) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_name( teb, params->buffer );
     wow64_glNamedBufferStorage( teb, params->buffer, (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->data), params->flags, funcs->p_glNamedBufferStorageEXT );
-    pthread_mutex_unlock( &wgl_lock );
-    set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
-    return STATUS_SUCCESS;
-}
-
-static NTSTATUS wow64_ext_glNamedBufferStorageExternalEXT( void *args )
-{
-    struct
-    {
-        PTR32 teb;
-        GLuint buffer;
-        PTR32 offset;
-        PTR32 size;
-        PTR32 clientBuffer;
-        GLbitfield flags;
-    } *params = args;
-    TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
-    const struct opengl_funcs *funcs = teb->glTable;
-    if (!funcs->p_glNamedBufferStorageExternalEXT) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_name( teb, params->buffer );
-    funcs->p_glNamedBufferStorageExternalEXT( params->buffer, (GLintptr)ULongToPtr(params->offset), (GLsizeiptr)ULongToPtr(params->size), ULongToPtr(params->clientBuffer), params->flags );
-    pthread_mutex_unlock( &wgl_lock );
     set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
     return STATUS_SUCCESS;
 }
 
@@ -64388,15 +64268,10 @@ static NTSTATUS wow64_ext_glNamedBufferStorageMemEXT( void *args )
         GLuint64 offset;
     } *params = args;
     TEB *teb = get_teb64( params->teb );
-    struct buffer *buffer;
     const struct opengl_funcs *funcs = teb->glTable;
     if (!funcs->p_glNamedBufferStorageMemEXT) return STATUS_NOT_IMPLEMENTED;
-    pthread_mutex_lock( &wgl_lock );
-    buffer = invalidate_buffer_name( teb, params->buffer );
-    funcs->p_glNamedBufferStorageMemEXT( params->buffer, (GLsizeiptr)ULongToPtr(params->size), params->memory, params->offset );
-    pthread_mutex_unlock( &wgl_lock );
+    wow64_glNamedBufferStorageMemEXT( teb, params->buffer, (GLsizeiptr)ULongToPtr(params->size), params->memory, params->offset, funcs->p_glNamedBufferStorageMemEXT );
     set_context_attribute( teb, -1 /* unsupported */, NULL, 0 );
-    if (buffer) free_buffer( funcs, buffer );
     return STATUS_SUCCESS;
 }
 
@@ -87322,7 +87197,6 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
     wow64_ext_glBufferParameteriAPPLE,
     wow64_ext_glBufferRegionEnabled,
     wow64_ext_glBufferStorage,
-    wow64_ext_glBufferStorageExternalEXT,
     wow64_ext_glBufferStorageMemEXT,
     wow64_ext_glBufferSubData,
     wow64_ext_glBufferSubDataARB,
@@ -88643,7 +88517,6 @@ const unixlib_entry_t __wine_unix_call_wow64_funcs[] =
     wow64_ext_glNamedBufferPageCommitmentMemNV,
     wow64_ext_glNamedBufferStorage,
     wow64_ext_glNamedBufferStorageEXT,
-    wow64_ext_glNamedBufferStorageExternalEXT,
     wow64_ext_glNamedBufferStorageMemEXT,
     wow64_ext_glNamedBufferSubData,
     wow64_ext_glNamedBufferSubDataEXT,
diff --git a/dlls/opengl32/unix_thunks.h b/dlls/opengl32/unix_thunks.h
index bc83223cdef0..1ba8ad4c5a0d 100644
--- a/dlls/opengl32/unix_thunks.h
+++ b/dlls/opengl32/unix_thunks.h
@@ -39,7 +39,10 @@ extern HPBUFFERARB wrap_wglCreatePbufferARB( TEB *teb, HDC hDC, int iPixelFormat
 extern BOOL wrap_wglMakeContextCurrentARB( TEB *teb, HDC hDrawDC, HDC hReadDC, HGLRC hglrc );
 
 #ifdef _WIN64
+extern void wow64_glBufferAttachMemoryNV( TEB *teb, GLenum target, GLuint memory, GLuint64 offset, PFN_glBufferAttachMemoryNV func );
+extern void wow64_glBufferData( TEB *teb, GLenum target, GLsizeiptr size, const void *data, GLenum usage, PFN_glBufferData func );
 extern void wow64_glBufferStorage( TEB *teb, GLenum target, GLsizeiptr size, const void *data, GLbitfield flags, PFN_glBufferStorage func );
+extern void wow64_glBufferStorageMemEXT( TEB *teb, GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset, PFN_glBufferStorageMemEXT func );
 extern void wow64_glDeleteBuffers( TEB *teb, GLsizei n, const GLuint *buffers, PFN_glDeleteBuffers func );
 extern void wow64_glFlushMappedBufferRange( TEB *teb, GLenum target, GLintptr offset, GLsizeiptr length, PFN_glFlushMappedBufferRange func );
 extern void wow64_glFlushMappedNamedBufferRange( TEB *teb, GLuint buffer, GLintptr offset, GLsizeiptr length, PFN_glFlushMappedNamedBufferRange func );
@@ -49,7 +52,10 @@ extern void *wow64_glMapBuffer( TEB *teb, GLenum target, GLenum access, PFN_glMa
 extern void *wow64_glMapBufferRange( TEB *teb, GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access, PFN_glMapBufferRange func );
 extern void *wow64_glMapNamedBuffer( TEB *teb, GLuint buffer, GLenum access, PFN_glMapNamedBuffer func );
 extern void *wow64_glMapNamedBufferRange( TEB *teb, GLuint buffer, GLintptr offset, GLsizeiptr length, GLbitfield access, PFN_glMapNamedBufferRange func );
+extern void wow64_glNamedBufferAttachMemoryNV( TEB *teb, GLuint buffer, GLuint memory, GLuint64 offset, PFN_glNamedBufferAttachMemoryNV func );
+extern void wow64_glNamedBufferData( TEB *teb, GLuint buffer, GLsizeiptr size, const void *data, GLenum usage, PFN_glNamedBufferData func );
 extern void wow64_glNamedBufferStorage( TEB *teb, GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags, PFN_glNamedBufferStorage func );
+extern void wow64_glNamedBufferStorageMemEXT( TEB *teb, GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset, PFN_glNamedBufferStorageMemEXT func );
 extern GLboolean wow64_glUnmapBuffer( TEB *teb, GLenum target, PFN_glUnmapBuffer func );
 extern GLboolean wow64_glUnmapNamedBuffer( TEB *teb, GLuint buffer, PFN_glUnmapNamedBuffer func );
 #endif
diff --git a/dlls/opengl32/unix_wgl.c b/dlls/opengl32/unix_wgl.c
index 46a8bb21abfd..0ba0282401d4 100644
--- a/dlls/opengl32/unix_wgl.c
+++ b/dlls/opengl32/unix_wgl.c
@@ -53,7 +53,6 @@ static BOOL is_wow64(void)
 }
 
 static UINT64 call_gl_debug_message_callback;
-pthread_mutex_t wgl_lock = PTHREAD_MUTEX_INITIALIZER;
 
 /* context state management */
 
@@ -115,23 +114,14 @@ struct hint_state
     GLenum multisample_nv;
 };
 
-struct buffers
-{
-    unsigned int ref;
-    struct rb_tree map;
-    struct vk_device *vk_device;
-};
-
 struct context
 {
     struct opengl_context base;
 
-    HGLRC client;                  /* client-side context handle */
     UINT64 debug_callback;         /* client pointer */
     UINT64 debug_user;             /* client pointer */
     GLubyte *extensions;           /* extension string */
     char *wow64_version;           /* wow64 GL version override */
-    struct buffers *buffers;       /* wow64 buffers map */
     BOOL use_pinned_memory;        /* use GL_AMD_pinned_memory to emulate persistent maps */
 
     /* semi-stub state tracker for wglCopyContext */
@@ -164,7 +154,6 @@ struct buffer
     struct vk_device *vk_device;
     VkDeviceMemory vk_memory;
     GLuint gl_memory;
-    GLbitfield flags;
 };
 
 struct vk_device
@@ -185,7 +174,6 @@ struct vk_device
 
 static ULONG_PTR zero_bits;
 
-static struct buffers *buffers;
 static const struct vulkan_funcs *vk_funcs;
 static VkInstance vk_instance;
 static PFN_vkDestroyInstance p_vkDestroyInstance;
@@ -368,7 +356,7 @@ static BOOL copy_context_attributes( TEB *teb, HGLRC client_dst, struct context
     dst->used |= (src->used & mask);
 
     if (!old_ctx) funcs->p_wglMakeContextCurrentARB( NULL, NULL, NULL );
-    else old_funcs->p_wglMakeContextCurrentARB( draw_hdc, read_hdc, old_ctx->client );
+    else old_funcs->p_wglMakeContextCurrentARB( draw_hdc, read_hdc, old_ctx->base.client_context );
 
     NtUserReleaseDC( hwnd, hdc );
     NtUserDestroyWindow( hwnd );
@@ -396,41 +384,24 @@ static int compare_buffer_name( const void *key, const struct rb_entry *entry )
     return memcmp( key, &buffer->name, sizeof(buffer->name) );
 }
 
-void free_buffer( const struct opengl_funcs *funcs, struct buffer *buffer )
+static struct
+{
+    struct rb_tree map;
+    struct vk_device *vk_device;
+} buffers = { .map = { compare_buffer_name } };
+
+static void free_buffer( const struct opengl_funcs *funcs, struct buffer *buffer )
 {
     if (buffer->vk_memory)
     {
         if (buffer->map_ptr) unmap_vk_buffer( buffer );
         buffer->vk_device->p_vkFreeMemory( buffer->vk_device->vk_device, buffer->vk_memory, NULL );
     }
-    if (buffer->gl_memory) funcs->p_glDeleteMemoryObjectsEXT( 1, &buffer->gl_memory );
+    if (buffer->gl_memory && funcs) funcs->p_glDeleteMemoryObjectsEXT( 1, &buffer->gl_memory );
     if (buffer->vm_ptr) NtFreeVirtualMemory( GetCurrentProcess(), &buffer->vm_ptr, &buffer->vm_size, MEM_RELEASE );
     free( buffer );
 }
 
-static void release_buffers( const struct opengl_funcs *funcs )
-{
-    struct buffer *buffer, *next;
-
-    if (--buffers->ref) return;
-
-    RB_FOR_EACH_ENTRY_DESTRUCTOR( buffer, next, &buffers->map, struct buffer, entry )
-        free_buffer( funcs, buffer );
-    free( buffers );
-    buffers = NULL;
-}
-
-static struct buffers *acquire_buffers(void)
-{
-    if (buffers) buffers->ref++;
-    else if ((buffers = calloc( 1, sizeof(*buffers ))))
-    {
-        buffers->ref = 1;
-        rb_init( &buffers->map, compare_buffer_name );
-    }
-    return buffers;
-}
-
 static struct context *context_from_client_context( HGLRC client_context )
 {
     struct opengl_context *base = opengl_context_from_handle( client_context );
@@ -830,7 +801,7 @@ static BOOL initialize_vk_device( TEB *teb, struct context *ctx )
     static PFN_vkGetPhysicalDeviceMemoryProperties p_vkGetPhysicalDeviceMemoryProperties;
     static PFN_vkGetPhysicalDeviceProperties2KHR p_vkGetPhysicalDeviceProperties2KHR;
 
-    if (buffers->vk_device) return TRUE; /* already initialized */
+    if (buffers.vk_device) return TRUE; /* already initialized */
     if (!client->extensions[GL_EXT_memory_object_win32] )
     {
         TRACE( "GL_EXT_memory_object_win32 is not supported\n" );
@@ -891,7 +862,7 @@ static BOOL initialize_vk_device( TEB *teb, struct context *ctx )
             vk_device = RB_ENTRY_VALUE( entry, struct vk_device, entry );
             if (!vk_device->vk_device) continue; /* known incompatible device */
             TRACE( "Found existing device %p for uuid %s\n", vk_device, debugstr_guid(&vk_device->uuid) );
-            buffers->vk_device = vk_device;
+            buffers.vk_device = vk_device;
             free( vk_physical_devices );
             return TRUE;
         }
@@ -1076,7 +1047,7 @@ static void make_context_current( TEB *teb, const struct opengl_funcs *funcs, HD
         TRACE( "-- %s (disabled by config)\n", all_extensions[i].name );
     }
 
-    if (is_win64 && ctx->buffers && !initialize_vk_device( teb, ctx )
+    if (is_win64 && is_wow64() && !initialize_vk_device( teb, ctx )
         && !(ctx->use_pinned_memory = client->extensions[GL_AMD_pinned_memory]))
     {
         if (client->major_version > 4 || (client->major_version == 4 && client->minor_version > 3))
@@ -1106,7 +1077,6 @@ static void make_context_current( TEB *teb, const struct opengl_funcs *funcs, HD
 
 static void free_context( const struct opengl_funcs *funcs, struct context *ctx )
 {
-    if (ctx->buffers) release_buffers( funcs );
     free( ctx->wow64_version );
     free( ctx->extensions );
     free( ctx );
@@ -1277,15 +1247,12 @@ HGLRC wrap_wglCreateContextAttribsARB( TEB *teb, HDC hdc, HGLRC client_shared, c
     }
     context->base.client_context = client_context;
 
-    if (is_win64 && is_wow64()) context->buffers = acquire_buffers();
-
     if (!(funcs->p_context_create( &context->base, hdc, attribs )))
     {
         free_context( funcs, context );
         return 0;
     }
 
-    context->client = client_context;
     opengl_client_context_init( client_context, context, funcs );
     return client_context;
 }
@@ -1751,8 +1718,15 @@ NTSTATUS thread_attach( void *args )
 
 NTSTATUS process_detach( void *args )
 {
-    struct vk_device *vk_device, *next;
+    struct vk_device *vk_device;
+    struct buffer *buffer;
+    void *next;
 
+    RB_FOR_EACH_ENTRY_DESTRUCTOR( buffer, next, &buffers.map, struct buffer, entry )
+    {
+        WARN( "Leaked buffer %p\n", buffer );
+        free_buffer( NULL, buffer );
+    }
     RB_FOR_EACH_ENTRY_DESTRUCTOR( vk_device, next, &vk_devices, struct vk_device, entry )
     {
         if (vk_device->vk_device) vk_device->p_vkDestroyDevice( vk_device->vk_device, NULL );
@@ -1780,6 +1754,8 @@ NTSTATUS get_pixel_formats( void *args )
 
 #ifdef _WIN64
 
+static pthread_mutex_t wgl_lock = PTHREAD_MUTEX_INITIALIZER;
+
 struct wow64_string_entry
 {
     const char *str;
@@ -1856,37 +1832,44 @@ static GLuint get_target_name( TEB *teb, GLenum target )
     return name;
 }
 
-static struct buffer *get_named_buffer( TEB *teb, GLuint name )
+/* wgl_lock must be held */
+static struct buffer *set_buffer_storage( GLuint name, struct buffer *buffer )
 {
-    struct context *ctx = get_current_context( teb, NULL, NULL );
     struct rb_entry *entry;
+    assert( !buffer || buffer->name == name );
+    if ((entry = rb_get( &buffers.map, &name ))) rb_remove( &buffers.map, entry );
+    if (buffer) rb_put( &buffers.map, &buffer->name, &buffer->entry );
+    return entry ? RB_ENTRY_VALUE( entry, struct buffer, entry ) : NULL;
+}
 
-    if (ctx && (entry = rb_get( &ctx->buffers->map, &name )))
-        return RB_ENTRY_VALUE( entry, struct buffer, entry );
-    return NULL;
+static struct buffer *set_named_buffer_storage( TEB *teb, GLuint name, struct buffer *buffer )
+{
+    struct buffer *previous;
+    pthread_mutex_lock( &wgl_lock );
+    previous = set_buffer_storage( name, buffer );
+    pthread_mutex_unlock( &wgl_lock );
+    return previous;
 }
 
-struct buffer *invalidate_buffer_name( TEB *teb, GLuint name )
+static struct buffer *get_named_buffer_storage( TEB *teb, GLuint name )
 {
-    struct buffer *buffer = get_named_buffer( teb, name );
-    if (buffer)
-    {
-        struct context *ctx = get_current_context( teb, NULL, NULL );
-        rb_remove( &ctx->buffers->map, &buffer->entry );
-    }
-    return buffer;
+    struct rb_entry *entry;
+    pthread_mutex_lock( &wgl_lock );
+    entry = rb_get( &buffers.map, &name );
+    pthread_mutex_unlock( &wgl_lock );
+    return entry ? RB_ENTRY_VALUE( entry, struct buffer, entry ) : NULL;
 }
 
-struct buffer *invalidate_buffer_target( TEB *teb, GLenum target )
+static struct buffer *set_target_buffer_storage( TEB *teb, GLenum target, struct buffer *buffer )
 {
     GLuint name = get_target_name( teb, target );
-    return name ? invalidate_buffer_name( teb, name ) : NULL;
+    return name ? set_named_buffer_storage( teb, name, buffer ) : NULL;
 }
 
-static struct buffer *get_target_buffer( TEB *teb, GLenum target )
+static struct buffer *get_target_buffer_storage( TEB *teb, GLenum target )
 {
     GLuint name = get_target_name( teb, target );
-    return name ? get_named_buffer( teb, name ) : NULL;
+    return name ? get_named_buffer_storage( teb, name ) : NULL;
 }
 
 static BOOL use_driver_buffer_map( struct buffer *buffer )
@@ -1971,11 +1954,10 @@ static struct buffer *create_buffer_storage( TEB *teb, GLenum target, GLuint nam
     VkResult vr;
 
     if (!(flags & (GL_MAP_READ_BIT | GL_MAP_WRITE_BIT))) return NULL;
-    if ((!(vk_device = ctx->buffers->vk_device) || !vk_device->vk_device) && !ctx->use_pinned_memory) return NULL;
+    if ((!(vk_device = buffers.vk_device) || !vk_device->vk_device) && !ctx->use_pinned_memory) return NULL;
 
     if (!(buffer = calloc( 1, sizeof(*buffer) ))) return NULL;
     buffer->name = buffer_name;
-    buffer->flags = flags;
     buffer->size = size;
     buffer->vk_device = vk_device;
 
@@ -1991,7 +1973,6 @@ static struct buffer *create_buffer_storage( TEB *teb, GLenum target, GLuint nam
          * to support it. */
         funcs->p_glBindBuffer( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, buffer_name );
         funcs->p_glBufferData( GL_EXTERNAL_VIRTUAL_MEMORY_BUFFER_AMD, size, buffer->vm_ptr, GL_DYNAMIC_COPY );
-        rb_put( &ctx->buffers->map, &buffer->name, &buffer->entry );
         TRACE( "created buffer %p with pinned memory %p\n", buffer, buffer->vm_ptr );
         return buffer;
     }
@@ -2058,7 +2039,6 @@ static struct buffer *create_buffer_storage( TEB *teb, GLenum target, GLuint nam
         funcs->p_glNamedBufferStorageMemEXT( buffer->name, size, buffer->gl_memory, 0 );
     else
         funcs->p_glBufferStorageMemEXT( target, size, buffer->gl_memory, 0 );
-    rb_put( &ctx->buffers->map, &buffer->name, &buffer->entry );
     TRACE( "created buffer_storage %p\n", buffer );
     return buffer;
 }
@@ -2089,8 +2069,7 @@ static void *wow64_map_buffer( TEB *teb, struct buffer *buffer, GLenum target, G
             .size = VK_WHOLE_SIZE,
         };
         VkResult vr;
-        struct context *ctx = get_current_context( teb, NULL, NULL );
-        struct vk_device *vk_device = ctx->buffers->vk_device;
+        struct vk_device *vk_device = buffers.vk_device;
 
         if (!buffer_vm_alloc( teb, buffer, buffer->size )) return NULL;
         placed_info.pPlacedAddress = buffer->vm_ptr;
@@ -2117,7 +2096,7 @@ static void *wow64_map_buffer( TEB *teb, struct buffer *buffer, GLenum target, G
 
     if (!buffer)
     {
-        struct context *ctx = get_current_context( teb, NULL, NULL );
+        struct buffer *previous;
         GLint size;
 
         if (name) funcs->p_glGetNamedBufferParameteriv( name, GL_BUFFER_SIZE, &size );
@@ -2126,7 +2105,8 @@ static void *wow64_map_buffer( TEB *teb, struct buffer *buffer, GLenum target, G
         if (!(buffer = calloc( 1, sizeof(*buffer) ))) return NULL;
         buffer->name = name ? name : get_target_name( teb, target );
         buffer->size = size;
-        rb_put( &ctx->buffers->map, &buffer->name, &buffer->entry );
+        previous = set_named_buffer_storage( teb, buffer->name, buffer );
+        assert( !previous );
         TRACE( "allocated buffer %p for %u\n", buffer, buffer->name );
     }
 
@@ -2180,54 +2160,47 @@ static GLbitfield map_range_flags_from_map_flags( GLenum flags )
     }
 }
 
-void wow64_glDeleteBuffers( TEB *teb, GLsizei n, const GLuint *buffers, PFN_glDeleteBuffers p_glDeleteBuffers )
+void wow64_glDeleteBuffers( TEB *teb, GLsizei n, const GLuint *names, PFN_glDeleteBuffers p_glDeleteBuffers )
 {
     const struct opengl_funcs *funcs = teb->glTable;
-    struct buffer *buffer;
+    struct buffer **tmp;
     GLsizei i;
 
-    pthread_mutex_lock( &wgl_lock );
-
-    p_glDeleteBuffers( n, buffers );
-    for (i = 0; i < n; i++)
+    if ((tmp = malloc( n * sizeof(*tmp) )))
     {
-        if ((buffer = invalidate_buffer_name( teb, buffers[i] ))) free_buffer( funcs, buffer );
+        pthread_mutex_lock( &wgl_lock );
+        for (i = 0; i < n; i++) tmp[i] = names[i] ? set_buffer_storage( names[i], NULL ) : NULL;
+        pthread_mutex_unlock( &wgl_lock );
+
+        for (i = 0; i < n; i++) if (tmp[i]) free_buffer( funcs, tmp[i] );
+        free( tmp );
     }
 
-    pthread_mutex_unlock( &wgl_lock );
+    p_glDeleteBuffers( n, names );
 }
 
 void wow64_glBufferStorage( TEB *teb, GLenum target, GLsizeiptr size, const void *data,
                             GLbitfield flags, PFN_glBufferStorage p_glBufferStorage )
 {
-    struct buffer *buffer = NULL;
-
-    if (flags & GL_MAP_PERSISTENT_BIT)
-        buffer = create_buffer_storage( teb, target, 0, size, data, flags );
+    const struct opengl_funcs *funcs = teb->glTable;
+    struct buffer *buffer = NULL, *previous;
 
+    if (flags & GL_MAP_PERSISTENT_BIT) buffer = create_buffer_storage( teb, target, 0, size, data, flags );
+    previous = set_target_buffer_storage( teb, target, buffer );
     if (!buffer) p_glBufferStorage( target, size, data, flags );
+    if (previous) free_buffer( funcs, previous );
 }
 
 void wow64_glNamedBufferStorage( TEB *teb, GLuint name, GLsizeiptr size, const void *data,
                                  GLbitfield flags, PFN_glNamedBufferStorage p_glNamedBufferStorage )
 {
-    struct buffer *buffer = NULL;
-
-    if (flags & GL_MAP_PERSISTENT_BIT)
-        buffer = create_buffer_storage( teb, 0, name, size, data, flags );
+    const struct opengl_funcs *funcs = teb->glTable;
+    struct buffer *buffer = NULL, *previous;
 
+    if (flags & GL_MAP_PERSISTENT_BIT) buffer = create_buffer_storage( teb, 0, name, size, data, flags );
+    previous = set_named_buffer_storage( teb, name, buffer );
     if (!buffer) p_glNamedBufferStorage( name, size, data, flags );
-}
-
-void wow64_glNamedBufferStorageEXT( TEB *teb, GLuint name, GLsizeiptr size, const void *data,
-                                    GLbitfield flags, PFN_glNamedBufferStorageEXT p_glNamedBufferStorageEXT )
-{
-    struct buffer *buffer = NULL;
-
-    if (flags & GL_MAP_PERSISTENT_BIT)
-        buffer = create_buffer_storage( teb, 0, name, size, data, flags );
-
-    if (!buffer) p_glNamedBufferStorageEXT( name, size, data, flags );
+    if (previous) free_buffer( funcs, previous );
 }
 
 static BOOL wow64_gl_get_buffer_pointer_v( TEB *teb, GLenum target, GLuint name, GLenum pname, PTR32 *wow_ptr )
@@ -2236,15 +2209,11 @@ static BOOL wow64_gl_get_buffer_pointer_v( TEB *teb, GLenum target, GLuint name,
     BOOL ret = FALSE;
 
     if (pname != GL_BUFFER_MAP_POINTER) return FALSE;
-
-    pthread_mutex_lock( &wgl_lock );
-    buffer = name ? get_named_buffer( teb, name ) : get_target_buffer( teb, target );
-    if (buffer)
+    if ((buffer = name ? get_named_buffer_storage( teb, name ) : get_target_buffer_storage( teb, target )))
     {
         *wow_ptr = PtrToUlong( buffer->map_ptr );
         ret = TRUE;
     }
-    pthread_mutex_unlock( &wgl_lock );
     return ret;
 }
 
@@ -2256,15 +2225,6 @@ void wow64_glGetBufferPointerv( TEB *teb, GLenum target, GLenum pname, PTR32 *pa
     *params = PtrToUlong(ptr);
 }
 
-void wow64_glGetBufferPointervARB( TEB *teb, GLenum target, GLenum pname, PTR32 *params )
-{
-    const struct opengl_funcs *funcs = teb->glTable;
-    void *ptr;
-    if (wow64_gl_get_buffer_pointer_v( teb, target, 0, pname, params )) return;
-    funcs->p_glGetBufferPointerv( target, pname, &ptr );
-    *params = PtrToUlong(ptr);
-}
-
 void wow64_glGetNamedBufferPointerv( TEB *teb, GLuint buffer, GLenum pname, PTR32 *params,
                                      PFN_glGetNamedBufferPointerv p_glGetNamedBufferPointerv )
 {
@@ -2274,26 +2234,15 @@ void wow64_glGetNamedBufferPointerv( TEB *teb, GLuint buffer, GLenum pname, PTR3
     *params = PtrToUlong(ptr);
 }
 
-void wow64_glGetNamedBufferPointervEXT( TEB *teb, GLuint buffer, GLenum pname, PTR32 *params )
-{
-    const struct opengl_funcs *funcs = teb->glTable;
-    void *ptr;
-    if (buffer && wow64_gl_get_buffer_pointer_v( teb, 0, buffer, pname, params )) return;
-    funcs->p_glGetNamedBufferPointervEXT( buffer, pname, &ptr );
-    *params = PtrToUlong(ptr);
-}
-
 void *wow64_glMapBuffer( TEB *teb, GLenum target, GLenum access, PFN_glMapBuffer p_glMapBuffer )
 {
     GLbitfield range_access = map_range_flags_from_map_flags( access );
     struct buffer *buffer;
     void *ptr = NULL;
 
-    pthread_mutex_lock( &wgl_lock );
-    buffer = get_target_buffer( teb, target );
+    buffer = get_target_buffer_storage( teb, target );
     if (use_driver_buffer_map( buffer )) ptr = p_glMapBuffer( target, access );
     ptr = wow64_map_buffer( teb, buffer, target, 0, 0, 0, range_access, ptr );
-    pthread_mutex_unlock( &wgl_lock );
     return ptr;
 }
 
@@ -2303,11 +2252,9 @@ void *wow64_glMapBufferRange( TEB *teb, GLenum target, GLintptr offset, GLsizeip
     struct buffer *buffer;
     void *ptr = NULL;
 
-    pthread_mutex_lock( &wgl_lock );
-    buffer = get_target_buffer( teb, target );
+    buffer = get_target_buffer_storage( teb, target );
     if (use_driver_buffer_map( buffer )) ptr = p_glMapBufferRange( target, offset, length, access );
     ptr = wow64_map_buffer( teb, buffer, target, 0, offset, length, access, ptr );
-    pthread_mutex_unlock( &wgl_lock );
     return ptr;
 }
 
@@ -2317,11 +2264,9 @@ void *wow64_glMapNamedBuffer( TEB *teb, GLuint name, GLenum access, PFN_glMapNam
     struct buffer *buffer;
     void *ptr = NULL;
 
-    pthread_mutex_lock( &wgl_lock );
-    buffer = get_named_buffer( teb, name );
+    buffer = get_named_buffer_storage( teb, name );
     if (use_driver_buffer_map( buffer )) ptr = p_glMapNamedBuffer( name, access );
     ptr = wow64_map_buffer( teb, buffer, 0, name, 0, 0, range_access, ptr );
-    pthread_mutex_unlock( &wgl_lock );
     return ptr;
 }
 
@@ -2331,11 +2276,9 @@ void *wow64_glMapNamedBufferRange( TEB *teb, GLuint name, GLintptr offset, GLsiz
     struct buffer *buffer;
     void *ptr = NULL;
 
-    pthread_mutex_lock( &wgl_lock );
-    buffer = get_named_buffer( teb, name );
+    buffer = get_named_buffer_storage( teb, name );
     if (use_driver_buffer_map( buffer )) ptr = p_glMapNamedBufferRange( name, offset, length, access );
     ptr = wow64_map_buffer( teb, buffer, 0, name, offset, length, access, ptr );
-    pthread_mutex_unlock( &wgl_lock );
     return ptr;
 }
 
@@ -2368,10 +2311,8 @@ GLboolean wow64_glUnmapBuffer( TEB *teb, GLenum target, PFN_glUnmapBuffer p_glUn
     struct buffer *buffer;
     GLboolean ret;
 
-    pthread_mutex_lock( &wgl_lock );
-    if ((buffer = get_target_buffer( teb, target ))) ret = wow64_unmap_buffer( teb, buffer );
+    if ((buffer = get_target_buffer_storage( teb, target ))) ret = wow64_unmap_buffer( teb, buffer );
     if (use_driver_buffer_map( buffer )) ret = p_glUnmapBuffer( target );
-    pthread_mutex_unlock( &wgl_lock );
     return ret;
 }
 
@@ -2380,10 +2321,8 @@ GLboolean wow64_glUnmapNamedBuffer( TEB *teb, GLuint name, PFN_glUnmapBuffer p_g
     struct buffer *buffer;
     GLboolean ret;
 
-    pthread_mutex_lock( &wgl_lock );
-    if ((buffer = get_named_buffer( teb, name ))) ret = wow64_unmap_buffer( teb, buffer );
+    if ((buffer = get_named_buffer_storage( teb, name ))) ret = wow64_unmap_buffer( teb, buffer );
     if (use_driver_buffer_map( buffer )) ret = p_glUnmapNamedBuffer( name );
-    pthread_mutex_unlock( &wgl_lock );
     return ret;
 }
 
@@ -2392,10 +2331,8 @@ void wow64_glFlushMappedBufferRange( TEB *teb, GLenum target, GLintptr offset, G
 {
     struct buffer *buffer;
 
-    pthread_mutex_lock( &wgl_lock );
-    if ((buffer = get_target_buffer( teb, target ))) flush_buffer( teb, buffer, offset, length );
+    if ((buffer = get_target_buffer_storage( teb, target ))) flush_buffer( teb, buffer, offset, length );
     if (use_driver_buffer_map( buffer )) p_glFlushMappedBufferRange( target, offset, length );
-    pthread_mutex_unlock( &wgl_lock );
 }
 
 void wow64_glFlushMappedNamedBufferRange( TEB *teb, GLuint name, GLintptr offset, GLsizeiptr length,
@@ -2403,21 +2340,62 @@ void wow64_glFlushMappedNamedBufferRange( TEB *teb, GLuint name, GLintptr offset
 {
     struct buffer *buffer;
 
-    pthread_mutex_lock( &wgl_lock );
-    if ((buffer = get_named_buffer( teb, name ))) flush_buffer( teb, buffer, offset, length );
+    if ((buffer = get_named_buffer_storage( teb, name ))) flush_buffer( teb, buffer, offset, length );
     if (use_driver_buffer_map( buffer )) p_glFlushMappedNamedBufferRange( name, offset, length );
-    pthread_mutex_unlock( &wgl_lock );
 }
 
-void wow64_glFlushMappedNamedBufferRangeEXT( TEB *teb, GLuint name, GLintptr offset, GLsizeiptr length )
+void wow64_glBufferAttachMemoryNV( TEB *teb, GLenum target, GLuint memory, GLuint64 offset, PFN_glBufferAttachMemoryNV p_glBufferAttachMemoryNV )
 {
     const struct opengl_funcs *funcs = teb->glTable;
     struct buffer *buffer;
 
-    pthread_mutex_lock( &wgl_lock );
-    if ((buffer = get_named_buffer( teb, name ))) flush_buffer( teb, buffer, offset, length );
-    if (use_driver_buffer_map( buffer )) funcs->p_glFlushMappedNamedBufferRangeEXT( name, offset, length );
-    pthread_mutex_unlock( &wgl_lock );
+    if ((buffer = set_target_buffer_storage( teb, target, NULL ))) free_buffer( funcs, buffer );
+    p_glBufferAttachMemoryNV( target, memory, offset );
+}
+
+void wow64_glBufferData( TEB *teb, GLenum target, GLsizeiptr size, const void *data, GLenum usage, PFN_glBufferData p_glBufferData )
+{
+    const struct opengl_funcs *funcs = teb->glTable;
+    struct buffer *buffer;
+
+    if ((buffer = set_target_buffer_storage( teb, target, NULL ))) free_buffer( funcs, buffer );
+    p_glBufferData( target, size, data, usage );
+}
+
+void wow64_glBufferStorageMemEXT( TEB *teb, GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset, PFN_glBufferStorageMemEXT p_glBufferStorageMemEXT )
+{
+    const struct opengl_funcs *funcs = teb->glTable;
+    struct buffer *buffer;
+
+    if ((buffer = set_target_buffer_storage( teb, target, NULL ))) free_buffer( funcs, buffer );
+    p_glBufferStorageMemEXT( target, size, memory, offset );
+}
+
+void wow64_glNamedBufferAttachMemoryNV( TEB *teb, GLuint name, GLuint memory, GLuint64 offset, PFN_glNamedBufferAttachMemoryNV p_glNamedBufferAttachMemoryNV )
+{
+    const struct opengl_funcs *funcs = teb->glTable;
+    struct buffer *buffer;
+
+    if ((buffer = set_named_buffer_storage( teb, name, NULL ))) free_buffer( funcs, buffer );
+    p_glNamedBufferAttachMemoryNV( name, memory, offset );
+}
+
+void wow64_glNamedBufferData( TEB *teb, GLuint name, GLsizeiptr size, const void *data, GLenum usage, PFN_glNamedBufferData p_glNamedBufferData )
+{
+    const struct opengl_funcs *funcs = teb->glTable;
+    struct buffer *buffer;
+
+    if ((buffer = set_named_buffer_storage( teb, name, NULL ))) free_buffer( funcs, buffer );
+    p_glNamedBufferData( name, size, data, usage );
+}
+
+void wow64_glNamedBufferStorageMemEXT( TEB *teb, GLuint name, GLsizeiptr size, GLuint memory, GLuint64 offset, PFN_glNamedBufferStorageMemEXT p_glNamedBufferStorageMemEXT )
+{
+    const struct opengl_funcs *funcs = teb->glTable;
+    struct buffer *buffer;
+
+    if ((buffer = set_named_buffer_storage( teb, name, NULL ))) free_buffer( funcs, buffer );
+    p_glNamedBufferStorageMemEXT( name, size, memory, offset );
 }
 
 NTSTATUS wow64_thread_attach( void *args )
diff --git a/dlls/opengl32/unixlib.h b/dlls/opengl32/unixlib.h
index 8f5f6391bbef..6df1d2fafd44 100644
--- a/dlls/opengl32/unixlib.h
+++ b/dlls/opengl32/unixlib.h
@@ -3699,16 +3699,6 @@ struct glBufferStorage_params
     GLbitfield flags;
 };
 
-struct glBufferStorageExternalEXT_params
-{
-    TEB *teb;
-    GLenum target;
-    GLintptr offset;
-    GLsizeiptr size;
-    GLeglClientBufferEXT clientBuffer;
-    GLbitfield flags;
-};
-
 struct glBufferStorageMemEXT_params
 {
     TEB *teb;
@@ -14898,16 +14888,6 @@ struct glNamedBufferStorageEXT_params
     GLbitfield flags;
 };
 
-struct glNamedBufferStorageExternalEXT_params
-{
-    TEB *teb;
-    GLuint buffer;
-    GLintptr offset;
-    GLsizeiptr size;
-    GLeglClientBufferEXT clientBuffer;
-    GLbitfield flags;
-};
-
 struct glNamedBufferStorageMemEXT_params
 {
     TEB *teb;
@@ -26293,7 +26273,6 @@ enum unix_funcs
     unix_glBufferParameteriAPPLE,
     unix_glBufferRegionEnabled,
     unix_glBufferStorage,
-    unix_glBufferStorageExternalEXT,
     unix_glBufferStorageMemEXT,
     unix_glBufferSubData,
     unix_glBufferSubDataARB,
@@ -27614,7 +27593,6 @@ enum unix_funcs
     unix_glNamedBufferPageCommitmentMemNV,
     unix_glNamedBufferStorage,
     unix_glNamedBufferStorageEXT,
-    unix_glNamedBufferStorageExternalEXT,
     unix_glNamedBufferStorageMemEXT,
     unix_glNamedBufferSubData,
     unix_glNamedBufferSubDataEXT,
diff --git a/dlls/opengl32/wgl.c b/dlls/opengl32/wgl.c
index 40f5ca818159..a132dd20f982 100644
--- a/dlls/opengl32/wgl.c
+++ b/dlls/opengl32/wgl.c
@@ -455,10 +455,10 @@ static void destroy_host_shader( struct object_table *table, GLuint host_id, GLu
     destroy_object( table->type, host_id );
 }
 
-static void free_object_table( struct object_table *table )
+static void free_object_table( struct object_table *table, BOOL destroy )
 {
-    if (table->type == OBJ_TYPE_SHADER) free_object_ids( table, table->host_ids, destroy_host_shader );
-    else free_object_ids( table, table->host_ids, destroy_host_object );
+    if (table->type == OBJ_TYPE_SHADER) free_object_ids( table, table->host_ids, destroy ? destroy_host_shader : NULL );
+    else free_object_ids( table, table->host_ids, destroy ? destroy_host_object : NULL );
     free_object_ids( table, table->client_ids, NULL );
 }
 
@@ -496,14 +496,14 @@ static struct display_lists *display_lists_acquire( struct display_lists *lists
     return lists;
 }
 
-static void display_lists_release( struct display_lists *lists )
+static void display_lists_release( struct display_lists *lists, BOOL destroy )
 {
     struct glDeleteSync_params delete_sync = { .teb = NtCurrentTeb() };
 
     if (InterlockedDecrement( &lists->refcount )) return;
 
     for (UINT i = 0; i < OBJ_TYPE_COUNT; i++)
-        free_object_table( lists->tables + i );
+        free_object_table( lists->tables + i, destroy );
 
     for (int i = 0; i < lists->syncs.count; i++)
     {
@@ -511,7 +511,7 @@ static void display_lists_release( struct display_lists *lists )
         if (LOWORD(entry->handle) == 0xffff) continue;
         WARN( "Leaking sync client %#x, host %p\n", entry->handle, entry->user_data );
         delete_sync.sync = entry->user_data;
-        UNIX_CALL( glDeleteSync, &delete_sync );
+        if (destroy) UNIX_CALL( glDeleteSync, &delete_sync );
         free( entry->user_data );
     }
 
@@ -557,7 +557,7 @@ static struct handle_entry *alloc_client_context( struct context *share )
     if (!(context->lists = share ? display_lists_acquire( share->lists ) : display_lists_create())) goto failed;
     if ((ptr = alloc_handle( &contexts, context ))) return ptr;
 
-    display_lists_release( context->lists );
+    display_lists_release( context->lists, share ? !share->base.broken_sharing : TRUE );
 failed:
     free( context );
     return NULL;
@@ -567,7 +567,7 @@ static void free_client_context( struct handle_entry *ptr )
 {
     struct context *context = context_from_opengl_client_context( ptr->context );
 
-    display_lists_release( context->lists );
+    display_lists_release( context->lists, !context->base.broken_sharing );
 
     free_handle( &contexts, ptr );
     free( context );
@@ -920,9 +920,14 @@ HGLRC WINAPI wglCreateContextAttribsARB( HDC hdc, HGLRC share, const int *attrib
         SetLastError( ERROR_INVALID_OPERATION );
         return NULL;
     }
+    if (share_context && share_context->base.broken_sharing)
+    {
+        ERR( "Shared context %p has broken display list sharing\n", share );
+        share = NULL;
+    }
     if (share) args.hShareContext = &share_context->base.obj;
 
-    if (!(ptr = alloc_client_context( share_context ))) return NULL;
+    if (!(ptr = alloc_client_context( share ? share_context : NULL ))) return NULL;
     args.ret = &ptr->context->obj;
 
     if ((status = UNIX_CALL( wglCreateContextAttribsARB, &args ))) WARN( "wglCreateContextAttribsARB returned %#lx\n", status );
@@ -1014,9 +1019,15 @@ BOOL WINAPI wglShareLists( HGLRC src_handle, HGLRC dst_handle )
     if (!(dst_context = context_from_handle( dst_handle ))) return FALSE;
     if (ReadNoFence( &dst_context->lists->modified )) return FALSE;
 
+    if (src_context->base.broken_sharing || dst_context->base.broken_sharing)
+    {
+        ERR( "Either source or destination context has broken sharing\n" );
+        return FALSE;
+    }
+
     lists = display_lists_acquire( src_context->lists );
     lists = InterlockedExchangePointer( (void *)&dst_context->lists, lists );
-    display_lists_release( lists );
+    display_lists_release( lists, TRUE );
 
     return TRUE;
 }
diff --git a/dlls/qcap/tests/audiorecord.c b/dlls/qcap/tests/audiorecord.c
index 11397531064e..55eb6ab6a49b 100644
--- a/dlls/qcap/tests/audiorecord.c
+++ b/dlls/qcap/tests/audiorecord.c
@@ -225,7 +225,6 @@ static ULONG WINAPI property_bag_Release(IPropertyBag *iface)
     ok(0, "Unexpected call.\n");
     return 1;
 }
-static int ppb_id;
 static unsigned int ppb_got_read;
 
 static HRESULT WINAPI property_bag_Read(IPropertyBag *iface, const WCHAR *name, VARIANT *var, IErrorLog *log)
@@ -270,7 +269,6 @@ static void test_property_bag(IMoniker *mon)
     VariantInit(&var);
     hr = IPropertyBag_Read(devenum_bag, L"WaveInId", &var, NULL);
     ok(hr == S_OK, "Got hr %#lx.\n", hr);
-    ppb_id = V_I4(&var);
 
     hr = CoCreateInstance(&CLSID_AudioRecord, NULL, CLSCTX_INPROC_SERVER,
             &IID_IPersistPropertyBag, (void **)&ppb);
diff --git a/dlls/secur32/lsa.c b/dlls/secur32/lsa.c
index 28390a993e47..7bf5f0043171 100644
--- a/dlls/secur32/lsa.c
+++ b/dlls/secur32/lsa.c
@@ -771,10 +771,16 @@ static SECURITY_STATUS WINAPI lsa_VerifySignature(CtxtHandle *context, SecBuffer
 
 static SECURITY_STATUS WINAPI lsa_QuerySecurityContextToken(CtxtHandle *context, HANDLE *token)
 {
+    HANDLE primary;
+    BOOL r;
+
     FIXME("%p %p): stub\n", context, token);
-    if (!OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, token))
+
+    if (!OpenProcessToken(GetCurrentProcess(), MAXIMUM_ALLOWED, &primary))
         return GetLastError();
-    return SEC_E_OK;
+    r = DuplicateToken(primary, SecurityImpersonation, token);
+    CloseHandle(primary);
+    return r ? SEC_E_OK : GetLastError();
 }
 
 static SECURITY_STATUS WINAPI lsa_EncryptMessage(CtxtHandle *context, ULONG quality_of_protection,
diff --git a/dlls/secur32/tests/ntlm.c b/dlls/secur32/tests/ntlm.c
index a5bb40ba2853..c2ae6f985186 100644
--- a/dlls/secur32/tests/ntlm.c
+++ b/dlls/secur32/tests/ntlm.c
@@ -1062,12 +1062,10 @@ static void test_Encrypt(void)
     BOOL                    first = TRUE;
     SspiData                client = {{0}}, server = {{0}};
     SecBufferDesc           crypt;
-    SecBuffer               data[2], complex_data[4];
+    SecBuffer               data[3] = {0}, complex_data[4] = {0};
     ULONG                   qop = 0xdeadbeef;
     SecPkgContext_Sizes     ctxt_sizes;
 
-    complex_data[1].pvBuffer = complex_data[3].pvBuffer = NULL;
-
     /****************************************************************
      * This is basically the same as in testAuth with a fake server,
      * as we need a valid, authenticated context.
@@ -1192,6 +1190,25 @@ static void test_Encrypt(void)
             getSecError(sec_status));
     ok(qop == 0xdeadbeef, "qop changed to %lu\n", qop);
 
+    /* Test decryption with multiple data buffers */
+    crypt.ulVersion = SECBUFFER_VERSION;
+    crypt.cBuffers = 2;
+    crypt.pBuffers = data;
+
+    sec_status = EncryptMessage(&client.ctxt, 0, &crypt, 0);
+    ok(sec_status == SEC_E_OK, "EncryptMessage returned %s, not SEC_E_OK.\n",
+            getSecError(sec_status));
+
+    crypt.cBuffers = 3;
+    data[2].BufferType = SECBUFFER_DATA;
+    data[2].cbBuffer = data[1].cbBuffer - 8;
+    data[2].pvBuffer = (BYTE *)data[1].pvBuffer + 8;
+    data[1].cbBuffer = 8;
+
+    sec_status = DecryptMessage(&server.ctxt, &crypt, 0, &qop);
+    ok(sec_status == SEC_E_OK, "DecryptMessage returned %s, not SEC_E_OK.\n", getSecError(sec_status));
+    ok(qop == 0xdeadbeef, "qop changed to %lu\n", qop);
+
 end:
     cleanupBuffers(&client);
     cleanupBuffers(&server);
@@ -1199,6 +1216,8 @@ end:
     DeleteSecurityContext(&client.ctxt);
     FreeCredentialsHandle(&client.cred);
 
+    HeapFree(GetProcessHeap(), 0, data[0].pvBuffer);
+    HeapFree(GetProcessHeap(), 0, data[1].pvBuffer);
     HeapFree(GetProcessHeap(), 0, complex_data[1].pvBuffer);
     HeapFree(GetProcessHeap(), 0, complex_data[3].pvBuffer);
 }
diff --git a/dlls/secur32/tests/schannel.c b/dlls/secur32/tests/schannel.c
index b5736a2ce397..8b1536d6c71b 100644
--- a/dlls/secur32/tests/schannel.c
+++ b/dlls/secur32/tests/schannel.c
@@ -536,7 +536,7 @@ static void test_remote_cert(PCCERT_CONTEXT remote_cert)
         cert_cnt++;
     }
 
-    ok(cert_cnt == 2, "cert_cnt = %u\n", cert_cnt);
+    ok(cert_cnt == 4, "cert_cnt = %u\n", cert_cnt);
     ok(incl_remote, "context does not contain cert itself\n");
 }
 
diff --git a/dlls/urlmon/tests/misc.c b/dlls/urlmon/tests/misc.c
index 2a61b8929303..1716af1766b0 100644
--- a/dlls/urlmon/tests/misc.c
+++ b/dlls/urlmon/tests/misc.c
@@ -28,6 +28,7 @@
 #include "winbase.h"
 #include "ole2.h"
 #include "urlmon.h"
+#include "wininet.h"
 
 #include "initguid.h"
 
@@ -2725,6 +2726,43 @@ static void test_bsc_marshaling(void)
     TerminateThread(thread, 0);
 }
 
+static void test_URLDownloadToCacheFile(void)
+{
+    WCHAR url[512], filename[MAX_PATH];
+    static const WCHAR prefix[] = L"http://127.0.0.1:1/test.";
+    char urlA[ARRAY_SIZE(url)], filenameA[MAX_PATH];
+    DWORD size;
+    HRESULT hres;
+    BOOL ret;
+    unsigned int i, len;
+
+    lstrcpyW(url, prefix);
+    len = lstrlenW(url);
+    for(i = len; i < ARRAY_SIZE(url) - 1; i++)
+        url[i] = 'a';
+    url[i] = 0;
+
+    memset(filename, 0xcc, sizeof(filename));
+    hres = URLDownloadToCacheFileW(NULL, url, filename, ARRAY_SIZE(filename), 0, NULL);
+    ok(FAILED(hres), "URLDownloadToCacheFileW returned %#lx\n", hres);
+    ok(filename[ARRAY_SIZE(filename) - 1] == 0xcccc, "filename buffer overflowed\n");
+
+    WideCharToMultiByte(CP_ACP, 0, url, -1, urlA, sizeof(urlA), NULL, NULL);
+    size = 0;
+    ret = GetUrlCacheEntryInfoA(urlA, NULL, &size);
+    ok(!ret && GetLastError() == ERROR_FILE_NOT_FOUND, "cache entry was not deleted, error %ld\n", GetLastError());
+    /* Always clean up any remaining cache entry */
+    if (ret || GetLastError() != ERROR_FILE_NOT_FOUND)
+        DeleteUrlCacheEntryA(urlA);
+
+    memset(filenameA, 0xcc, sizeof(filenameA));
+    hres = URLDownloadToCacheFileA(NULL, urlA, filenameA, sizeof(filenameA), 0, NULL);
+    ok(FAILED(hres), "URLDownloadToCacheFileA returned %#lx\n", hres);
+    ok(filenameA[sizeof(filenameA) - 1] == (char)0xcc, "filenameA buffer overflowed\n");
+    /* Always clean up any remaining cache entry */
+    DeleteUrlCacheEntryA(urlA);
+}
+
 static void test_MapBrowserEmulationModeToUserAgent(BOOL custom_ua)
 {
     /* Undocumented structure of unknown size, crashes if it's too small (with arbitrary values from stack) */
@@ -2832,6 +2870,7 @@ START_TEST(misc)
         test_MkParseDisplayNameEx();
         test_IsValidURL();
         test_bsc_marshaling();
+        test_URLDownloadToCacheFile();
         test_MapBrowserEmulationModeToUserAgent(TRUE);
     }
 
diff --git a/dlls/urlmon/umon.c b/dlls/urlmon/umon.c
index 0ee8502d0704..8f3fa892d838 100644
--- a/dlls/urlmon/umon.c
+++ b/dlls/urlmon/umon.c
@@ -850,7 +850,7 @@ HRESULT WINAPI URLDownloadToCacheFileA(LPUNKNOWN lpUnkCaller, LPCSTR szURL, LPST
     if(szFileName)
         file_name = malloc(dwBufLength * sizeof(WCHAR));
 
-    hres = URLDownloadToCacheFileW(lpUnkCaller, url, file_name, dwBufLength*sizeof(WCHAR),
+    hres = URLDownloadToCacheFileW(lpUnkCaller, url, file_name, dwBufLength,
             dwReserved, pBSC);
 
     if(SUCCEEDED(hres) && file_name)
@@ -887,8 +887,10 @@ HRESULT WINAPI URLDownloadToCacheFileW(LPUNKNOWN lpUnkCaller, LPCWSTR szURL, LPW
         return E_FAIL;
 
     hr = URLDownloadToFileW(lpUnkCaller, szURL, cache_path, 0, pBSC);
-    if (FAILED(hr))
+    if (FAILED(hr)) {
+        DeleteFileW(cache_path);
         return hr;
+    }
 
     expire.dwHighDateTime = 0;
     expire.dwLowDateTime = 0;
@@ -896,10 +898,12 @@ HRESULT WINAPI URLDownloadToCacheFileW(LPUNKNOWN lpUnkCaller, LPCWSTR szURL, LPW
     modified.dwLowDateTime = 0;
 
     if (!CommitUrlCacheEntryW(szURL, cache_path, expire, modified, NORMAL_CACHE_ENTRY,
-                              header, sizeof(header), NULL, NULL))
+                              header, sizeof(header), NULL, NULL)) {
+        DeleteFileW(cache_path);
         return E_FAIL;
+    }
 
-    if (lstrlenW(cache_path) > dwBufLength)
+    if (lstrlenW(cache_path) + 1 > dwBufLength)
         return E_OUTOFMEMORY;
 
     lstrcpyW(szFileName, cache_path);
diff --git a/dlls/user32/input.c b/dlls/user32/input.c
index 623fab14d113..051f4e48ec95 100644
--- a/dlls/user32/input.c
+++ b/dlls/user32/input.c
@@ -752,16 +752,20 @@ BOOL WINAPI GetPointerDeviceProperties( HANDLE device, UINT32 *count,
 
 BOOL WINAPI GetPointerPenInfo( UINT32 id, POINTER_PEN_INFO *info )
 {
-    FIXME( "id %u, info %p stub!\n", id, info );
-    SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
-    return FALSE;
+    UINT32 count = 1;
+
+    TRACE( "id %u, info %p\n", id, info );
+
+    return NtUserGetPointerInfoList( id, PT_PEN, 0, 0, sizeof(*info), &count, &count, info );
 }
 
 BOOL WINAPI GetPointerTouchInfo( UINT32 id, POINTER_TOUCH_INFO *info )
 {
-    FIXME( "id %u, info %p stub!\n", id, info );
-    SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
-    return FALSE;
+    UINT32 count = 1;
+
+    TRACE( "id %u, info %p stub!\n", id, info );
+
+    return NtUserGetPointerInfoList( id, PT_TOUCH, 0, 0, sizeof(*info), &count, &count, info );
 }
 
 BOOL WINAPI GetRawPointerDeviceData( UINT32 id, UINT32 hist_count, UINT32 prop_count,
@@ -775,9 +779,50 @@ BOOL WINAPI GetRawPointerDeviceData( UINT32 id, UINT32 hist_count, UINT32 prop_c
 
 BOOL WINAPI GetPointerTouchInfoHistory( UINT32 id, UINT32 *count, POINTER_TOUCH_INFO *info )
 {
-    FIXME( "id %u, count %p, info %p stub!\n", id, count, info );
-    SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
-    return FALSE;
+    UINT32 pointers = 1;
+
+    TRACE( "id %u, count %p, info %p\n", id, count, info );
+
+    return NtUserGetPointerInfoList( id, PT_TOUCH, 0, 0, sizeof(*info), count, &pointers, info );
+}
+
+BOOL WINAPI GetPointerPenInfoHistory( UINT32 id, UINT32 *count, POINTER_PEN_INFO *info )
+{
+    UINT32 pointers = 1;
+
+    TRACE( "id %u, count %p, info %p\n", id, count, info );
+
+    return NtUserGetPointerInfoList( id, PT_PEN, 0, 0, sizeof(*info), count, &pointers, info );
+}
+
+BOOL WINAPI GetPointerFrameTouchInfo( UINT32 id, UINT32 *count, POINTER_TOUCH_INFO *info )
+{
+    UINT32 entries = 1;
+
+    TRACE( "id %u, count %p, info %p\n", id, count, info );
+
+    return NtUserGetPointerInfoList( id, PT_TOUCH, 0, 0, sizeof(*info), &entries, count, info );
+}
+
+BOOL WINAPI GetPointerFramePenInfo( UINT32 id, UINT32 *count, POINTER_PEN_INFO *info )
+{
+    UINT32 entries = 1;
+
+    TRACE( "id %u, count %p, info %p\n", id, count, info );
+
+    return NtUserGetPointerInfoList( id, PT_PEN, 0, 0, sizeof(*info), &entries, count, info );
+}
+
+BOOL WINAPI GetPointerFrameTouchInfoHistory( UINT32 id, UINT32 *entries, UINT32 *pointers, POINTER_TOUCH_INFO *info )
+{
+    TRACE( "id %u, entries %p, pointers %p, info %p\n", id, entries, pointers, info );
+    return NtUserGetPointerInfoList( id, PT_TOUCH, 0, 0, sizeof(*info), entries, pointers, info );
+}
+
+BOOL WINAPI GetPointerFramePenInfoHistory( UINT32 id, UINT32 *entries, UINT32 *pointers, POINTER_PEN_INFO *info )
+{
+    TRACE( "id %u, entries %p, pointers %p, info %p\n", id, entries, pointers, info );
+    return NtUserGetPointerInfoList( id, PT_PEN, 0, 0, sizeof(*info), entries, pointers, info );
 }
 
 
diff --git a/dlls/user32/misc.c b/dlls/user32/misc.c
index 5e26601d2342..0fa04b855dae 100644
--- a/dlls/user32/misc.c
+++ b/dlls/user32/misc.c
@@ -516,29 +516,38 @@ LRESULT WINAPI PackTouchHitTestingProximityEvaluation(const TOUCH_HIT_TESTING_IN
     return 0;
 }
 
-/**********************************************************************
- * GetPointerType [USER32.@]
- */
-BOOL WINAPI GetPointerType(UINT32 id, POINTER_INPUT_TYPE *type)
+BOOL WINAPI GetPointerInfo(UINT32 id, POINTER_INFO *info)
 {
-    FIXME("(%d %p): stub\n", id, type);
+    UINT32 count = 1;
 
-    if(!id || !type)
-    {
-        SetLastError(ERROR_INVALID_PARAMETER);
-        return FALSE;
-    }
+    TRACE( "id %d, info %p\n", id, info );
 
-    *type = PT_MOUSE;
-    return TRUE;
+    return NtUserGetPointerInfoList( id, PT_POINTER, 0, 0, sizeof(*info), &count, &count, info );
 }
 
-BOOL WINAPI GetPointerInfo(UINT32 id, POINTER_INFO *info)
+BOOL WINAPI GetPointerInfoHistory( UINT32 id, UINT32 *count, POINTER_INFO *info )
 {
-    FIXME("(%d %p): stub\n", id, info);
+    UINT32 pointers = 1;
 
-    SetLastError(ERROR_INVALID_PARAMETER);
-    return FALSE;
+    TRACE( "id %u, count %p, info %p\n", id, count, info );
+
+    return NtUserGetPointerInfoList( id, PT_POINTER, 0, 0, sizeof(*info), count, &pointers, info );
+}
+
+BOOL WINAPI GetPointerFrameInfo( UINT32 id, UINT32 *count, POINTER_INFO *info )
+{
+    UINT32 entries = 1;
+
+    TRACE( "id %u, count %p, info %p\n", id, count, info );
+
+    return NtUserGetPointerInfoList( id, PT_POINTER, 0, 0, sizeof(*info), &entries, count, info );
+}
+
+BOOL WINAPI GetPointerFrameInfoHistory( UINT32 id, UINT32 *entries, UINT32 *pointers, POINTER_INFO *info )
+{
+    TRACE( "id %u, entries %p, pointers %p, info %p\n", id, entries, pointers, info );
+
+    return NtUserGetPointerInfoList( id, PT_POINTER, 0, 0, sizeof(*info), entries, pointers, info );
 }
 
 LRESULT WINAPI ImeWndProcA( HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam )
diff --git a/dlls/user32/tests/input.c b/dlls/user32/tests/input.c
index 6ad01e829783..382703452d3a 100644
--- a/dlls/user32/tests/input.c
+++ b/dlls/user32/tests/input.c
@@ -1313,6 +1313,27 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W
         {0},
     };
 
+    struct send_input_keyboard_test pause_scan[] =
+    {
+        {.scan = 0x21d, .flags = KEYEVENTF_SCANCODE, .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80},
+         .expect = {KEY_HOOK(WM_KEYDOWN, 0x1d, VK_LCONTROL), KEY_MSG(WM_KEYDOWN, 0x1d, VK_CONTROL), {0}}},
+        {.scan = 0x21d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, .expect_state = {[VK_CONTROL] = 0x01, [VK_LCONTROL] = 0x01},
+         .expect = {KEY_HOOK(WM_KEYUP, 0x1d, VK_LCONTROL), KEY_MSG(WM_KEYUP, 0x1d, VK_CONTROL), {0}}},
+        {.scan = 0xe11d, .flags = KEYEVENTF_SCANCODE, .expect_state = {[VK_CONTROL] = 0x80, [VK_LCONTROL] = 0x80},
+         .expect = {KEY_HOOK(WM_KEYDOWN, 0x1d, VK_LCONTROL), KEY_MSG(WM_KEYDOWN, 0x1d, VK_CONTROL), {0}}},
+        {.scan = 0xe11d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_KEYUP, .expect_state = {[VK_CONTROL] = 0x01, [VK_LCONTROL] = 0x01},
+         .expect = {KEY_HOOK(WM_KEYUP, 0x1d, VK_LCONTROL), KEY_MSG(WM_KEYUP, 0x1d, VK_CONTROL), {0}}},
+        {.scan = 0xe11d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY, .expect_state = {[VK_CONTROL] = 0x80, [VK_RCONTROL] = 0x80},
+         .expect = {KEY_HOOK_(WM_KEYDOWN, 0x1d, VK_RCONTROL, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYDOWN, 0x11d, VK_CONTROL), {0}}},
+        {.scan = 0xe11d, .flags = KEYEVENTF_SCANCODE | KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, .expect_state = {[VK_CONTROL] = 0x01, [VK_RCONTROL] = 0x01},
+         .expect = {KEY_HOOK_(WM_KEYUP, 0x1d, VK_RCONTROL, LLKHF_EXTENDED, .todo_value = TRUE), KEY_MSG(WM_KEYUP, 0x11d, VK_CONTROL), {0}}},
+        {.vkey = VK_PAUSE, .expect_state = {[VK_PAUSE] = 0x80},
+         .expect = {KEY_HOOK(WM_KEYDOWN, 0x7, VK_PAUSE), KEY_MSG(WM_KEYDOWN, 0x7, VK_PAUSE), {0}}},
+        {.vkey = VK_PAUSE, .flags = KEYEVENTF_KEYUP, .expect_state = {[VK_PAUSE] = 0x01},
+         .expect = {KEY_HOOK(WM_KEYUP, 0x8, VK_PAUSE), KEY_MSG(WM_KEYUP, 0x8, VK_PAUSE), {0}}},
+        {0},
+    };
+
 #undef WIN_MSG
 #undef KBD_HOOK
 #undef KEY_HOOK_
@@ -1397,6 +1418,7 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W
     check_send_input_keyboard_test( unicode_vkey_packet, TRUE );
     check_send_input_keyboard_test( numpad_scan, TRUE );
     check_send_input_keyboard_test( numpad_scan_numlock, TRUE );
+    check_send_input_keyboard_test( pause_scan, TRUE );
     winetest_pop_context();
 
     wait_messages( 100, FALSE );
@@ -1444,6 +1466,7 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W
     check_send_input_keyboard_test( unicode_vkey_packet, FALSE );
     check_send_input_keyboard_test( numpad_scan, FALSE );
     check_send_input_keyboard_test( numpad_scan_numlock, FALSE );
+    check_send_input_keyboard_test( pause_scan, FALSE );
     winetest_pop_context();
 
     ok_ret( 1, DestroyWindow( hwnd ) );
@@ -1456,6 +1479,36 @@ static void test_SendInput_keyboard_messages( WORD vkey, WORD scan, WCHAR wch, W
 
 static void test_keynames(void)
 {
+    static const struct
+    {
+        LONG lparam;
+        const char *expected_name;
+        BOOL todo;
+        BOOL todo_value;
+    } tests[] =
+    {
+        {0x00370000, "Num *", .todo_value = TRUE},
+        {0x01370000, "Prnt Scrn", .todo_value = TRUE},
+        {0x02370000, "Num *", .todo_value = TRUE},
+        {0xe0370000, "Num *", .todo_value = TRUE},
+        {0xe1370000, "Prnt Scrn", .todo_value = TRUE},
+        {0x00450000, "Pause", .todo_value = TRUE},
+        {0x01450000, "Num Lock"},
+        {0x02450000, "Pause", .todo_value = TRUE},
+        {0xe0450000, "Pause", .todo_value = TRUE},
+        {0xe1450000, "Num Lock"},
+        {0x00460000, "Scroll Lock", .todo_value = TRUE},
+        {0x01460000, "Break", .todo_value = TRUE},
+        {0xe0460000, "Scroll Lock", .todo_value = TRUE},
+        {0xe1460000, "Break", .todo_value = TRUE},
+        {0x01480000, "Up"},
+        {0x001d0000, "Ctrl", .todo_value = TRUE},
+        {0x011d0000, "Right Ctrl", .todo_value = TRUE},
+        {0x021d0000, "Ctrl", .todo_value = TRUE},
+        {0xe01d0000, "Ctrl", .todo_value = TRUE},
+        {0xe11d0000, "Right Ctrl", .todo_value = TRUE},
+    };
+    BOOL us_kbd = (GetKeyboardLayout(0) == (HKL)(ULONG_PTR)0x04090409);
     int i, len;
     char buff[256];
 
@@ -1465,6 +1518,18 @@ static void test_keynames(void)
         len = GetKeyNameTextA(i << 16, buff, sizeof(buff));
         ok(len || !buff[0], "%d: Buffer is not zeroed\n", i);
     }
+
+    if (!us_kbd) skip("skipping test with inconsistent results on non-us keyboard\n");
+    else for (i = 0; i < ARRAY_SIZE(tests); i++)
+    {
+        winetest_push_context("scancode %04x", (unsigned int)tests[i].lparam >> 16);
+        len = GetKeyNameTextA(tests[i].lparam, buff, sizeof(buff));
+        todo_wine_if(tests[i].todo) ok(len, "No key name\n");
+        todo_wine_if(tests[i].todo_value)
+        ok(!strcmp(buff, tests[i].expected_name), "Unexpected key name %s\n", debugstr_a(buff));
+        trace("name %s\n", debugstr_a(buff));
+        winetest_pop_context();
+    }
 }
 
 static BOOL accept_keyboard_messages_raw( UINT msg )
@@ -3196,10 +3261,65 @@ static void test_DefRawInputProc(void)
     ok(GetLastError() == 0xdeadbeef, "got %ld\n", GetLastError());
 }
 
+static const char *debug_map_type(UINT map_type)
+{
+#define MAP_TO_STR(x) case x: return #x
+    switch (map_type)
+    {
+        MAP_TO_STR(MAPVK_VK_TO_VSC);
+        MAP_TO_STR(MAPVK_VSC_TO_VK);
+        MAP_TO_STR(MAPVK_VK_TO_CHAR);
+        MAP_TO_STR(MAPVK_VSC_TO_VK_EX);
+        MAP_TO_STR(MAPVK_VK_TO_VSC_EX);
+    default:
+        return "<unknown>";
+    }
+#undef MAP_TO_STR
+}
+
 static void test_key_map(void)
 {
+    static const struct
+    {
+        UINT input;
+        UINT map_type;
+        UINT expected;
+        BOOL todo;
+    } tests[] =
+    {
+        {0x136, MAPVK_VSC_TO_VK_EX, 0, TRUE},
+        {0xe036, MAPVK_VSC_TO_VK_EX, 0, TRUE},
+        {0xe136, MAPVK_VSC_TO_VK_EX, 0, TRUE},
+        {0x37, MAPVK_VSC_TO_VK_EX, VK_MULTIPLY, TRUE},
+        {0x137, MAPVK_VSC_TO_VK_EX, 0, TRUE},
+        {0x237, MAPVK_VSC_TO_VK_EX, 0, TRUE},
+        {0xe037, MAPVK_VSC_TO_VK_EX, VK_SNAPSHOT},
+        {0xe137, MAPVK_VSC_TO_VK_EX, 0, TRUE},
+        {0x45, MAPVK_VSC_TO_VK_EX, VK_NUMLOCK},
+        {0x145, MAPVK_VSC_TO_VK_EX, 0, TRUE},
+        {0xe045, MAPVK_VSC_TO_VK_EX, 0, TRUE},
+        {0xe145, MAPVK_VSC_TO_VK_EX, 0, TRUE},
+        {0x1d, MAPVK_VSC_TO_VK_EX, VK_LCONTROL},
+        {0x011d, MAPVK_VSC_TO_VK_EX, 0, TRUE},
+        {0x021d, MAPVK_VSC_TO_VK_EX, 0, TRUE},
+        {0xe01d, MAPVK_VSC_TO_VK_EX, VK_RCONTROL, TRUE},
+        {0xe11d, MAPVK_VSC_TO_VK_EX, VK_PAUSE, TRUE},
+        {0x46, MAPVK_VSC_TO_VK_EX, VK_SCROLL},
+        {0xe046, MAPVK_VSC_TO_VK_EX, VK_CANCEL, TRUE},
+
+        {VK_RSHIFT, MAPVK_VK_TO_VSC_EX, 0x36},
+        {VK_MULTIPLY, MAPVK_VK_TO_VSC_EX, 0x37},
+        {VK_NUMLOCK, MAPVK_VK_TO_VSC_EX, 0x45},
+        {VK_UP, MAPVK_VK_TO_VSC_EX, 0x48},
+        {VK_LCONTROL, MAPVK_VK_TO_VSC_EX, 0x1d},
+        {VK_RCONTROL, MAPVK_VK_TO_VSC_EX, 0xe01d},
+        {VK_PAUSE, MAPVK_VK_TO_VSC_EX, 0xe11d, TRUE},
+        {VK_SCROLL, MAPVK_VK_TO_VSC_EX, 0x46},
+        {VK_CANCEL, MAPVK_VK_TO_VSC_EX, 0xe046, TRUE},
+        {VK_SNAPSHOT, MAPVK_VK_TO_VSC_EX, 0x54},
+    };
     HKL kl = GetKeyboardLayout(0);
-    UINT kL, kR, s, sL;
+    UINT kL, kR, s, sL, r;
     int i;
     static const UINT numpad_collisions[][2] = {
         { VK_NUMPAD0, VK_INSERT },
@@ -3256,6 +3376,13 @@ static void test_key_map(void)
     ok(s >> 8 == 0xE0 || broken(s == 0), "Scan code prefix for VK_RMENU should be 0xE0 when MAPVK_VK_TO_VSC_EX is set, was %#1x\n", s >> 8);
     s = MapVirtualKeyExA(VK_RSHIFT, MAPVK_VK_TO_VSC_EX, kl);
     ok(s >> 8 == 0x00 || broken(s == 0), "The scan code shouldn't have a prefix, got %#1x\n", s >> 8);
+
+    for (i = 0; i < ARRAY_SIZE(tests); i++)
+    {
+        r = MapVirtualKeyExA(tests[i].input, tests[i].map_type, kl);
+        todo_wine_if(tests[i].todo) ok(r == tests[i].expected, "Unexpected %s %x -> %x\n",
+                                       debug_map_type(tests[i].map_type), tests[i].input, r);
+    }
 }
 
 #define shift 1
@@ -5627,9 +5754,7 @@ static void test_GetPointerInfo( BOOL mouse_in_pointer_enabled )
     ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() );
     SetLastError( 0xdeadbeef );
     ret = pGetPointerType( 0xdead, &type );
-    todo_wine
     ok( !ret, "GetPointerType succeeded\n" );
-    todo_wine
     ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() );
     ret = pGetPointerType( 1, &type );
     ok( ret, "GetPointerType failed, error %lu\n", GetLastError() );
@@ -5754,7 +5879,6 @@ static void test_GetPointerInfo( BOOL mouse_in_pointer_enabled )
 
     memset( pointer_info, 0xcd, sizeof(pointer_info) );
     ret = pGetPointerInfo( 1, pointer_info );
-    todo_wine_if(mouse_in_pointer_enabled)
     ok( ret == mouse_in_pointer_enabled, "GetPointerInfo failed, error %lu\n", GetLastError() );
     if (!mouse_in_pointer_enabled)
     {
@@ -5762,18 +5886,13 @@ static void test_GetPointerInfo( BOOL mouse_in_pointer_enabled )
         return;
     }
 
-    todo_wine
     ok( pointer_info[0].pointerType == PT_MOUSE, "got pointerType %lu\n", pointer_info[0].pointerType );
-    todo_wine
     ok( pointer_info[0].pointerId == 1, "got pointerId %u\n", pointer_info[0].pointerId );
     ok( !!pointer_info[0].frameId, "got frameId %u\n", pointer_info[0].frameId );
-    todo_wine
     ok( pointer_info[0].pointerFlags == (0x40000 | POINTER_MESSAGE_FLAG_INRANGE),
         "got pointerFlags %#x\n", pointer_info[0].pointerFlags );
-    todo_wine
     ok( pointer_info[0].sourceDevice == INVALID_HANDLE_VALUE || broken(!!pointer_info[0].sourceDevice) /* < w10 & 32bit */,
         "got sourceDevice %p\n", pointer_info[0].sourceDevice );
-    todo_wine
     ok( pointer_info[0].hwndTarget == hwnd, "got hwndTarget %p\n", pointer_info[0].hwndTarget );
     ok( !!pointer_info[0].ptPixelLocation.x, "got ptPixelLocation %s\n", wine_dbgstr_point( &pointer_info[0].ptPixelLocation ) );
     ok( !!pointer_info[0].ptPixelLocation.y, "got ptPixelLocation %s\n", wine_dbgstr_point( &pointer_info[0].ptPixelLocation ) );
@@ -5784,14 +5903,10 @@ static void test_GetPointerInfo( BOOL mouse_in_pointer_enabled )
     ok( !!pointer_info[0].ptHimetricLocationRaw.x, "got ptHimetricLocationRaw %s\n", wine_dbgstr_point( &pointer_info[0].ptHimetricLocationRaw ) );
     ok( !!pointer_info[0].ptHimetricLocationRaw.y, "got ptHimetricLocationRaw %s\n", wine_dbgstr_point( &pointer_info[0].ptHimetricLocationRaw ) );
     ok( !!pointer_info[0].dwTime, "got dwTime %lu\n", pointer_info[0].dwTime );
-    todo_wine
     ok( pointer_info[0].historyCount == 1, "got historyCount %u\n", pointer_info[0].historyCount );
-    todo_wine
     ok( pointer_info[0].InputData == 0, "got InputData %u\n", pointer_info[0].InputData );
-    todo_wine
     ok( pointer_info[0].dwKeyStates == 0, "got dwKeyStates %lu\n", pointer_info[0].dwKeyStates );
     ok( !!pointer_info[0].PerformanceCount, "got PerformanceCount %I64u\n", pointer_info[0].PerformanceCount );
-    todo_wine
     ok( pointer_info[0].ButtonChangeType == POINTER_CHANGE_FIRSTBUTTON_UP, "got ButtonChangeType %u\n", pointer_info[0].ButtonChangeType );
 
     thread = CreateThread( NULL, 0, test_GetPointerInfo_thread, NULL, 0, NULL );
diff --git a/dlls/user32/tests/monitor.c b/dlls/user32/tests/monitor.c
index b5c8f5bc6298..e9cf9c131120 100644
--- a/dlls/user32/tests/monitor.c
+++ b/dlls/user32/tests/monitor.c
@@ -1615,11 +1615,7 @@ static void test_work_area(void)
     trace("min: %ld,%ld max %ld,%ld normal %s\n", wp.ptMinPosition.x, wp.ptMinPosition.y,
           wp.ptMaxPosition.x, wp.ptMaxPosition.y, wine_dbgstr_rect(&wp.rcNormalPosition));
     OffsetRect(&wp.rcNormalPosition, rc_work.left, rc_work.top);
-    todo_wine_if (mi.rcMonitor.left != mi.rcWork.left ||
-        mi.rcMonitor.top != mi.rcWork.top)  /* FIXME: remove once Wine is fixed */
-    {
-        ok(EqualRect(&rc_normal, &wp.rcNormalPosition), "normal pos is different\n");
-    }
+    ok(EqualRect(&rc_normal, &wp.rcNormalPosition), "normal pos is different\n");
 
     SetWindowLongA(hwnd, GWL_EXSTYLE, WS_EX_TOOLWINDOW);
 
diff --git a/dlls/user32/tests/msg.c b/dlls/user32/tests/msg.c
index 6fa3a3b6ce74..feae709ae2a4 100644
--- a/dlls/user32/tests/msg.c
+++ b/dlls/user32/tests/msg.c
@@ -15956,7 +15956,6 @@ static void test_ShowWindow(void)
        "expected -1,-1 got %ld,%ld\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
     ok(wp.ptMaxPosition.x == -1 && wp.ptMaxPosition.y == -1,
        "expected -1,-1 got %ld,%ld\n", wp.ptMaxPosition.x, wp.ptMaxPosition.y);
-    todo_wine_if (work_rc.left || work_rc.top) /* FIXME: remove once Wine is fixed */
     ok(EqualRect(&win_rc, &wp.rcNormalPosition), "expected %s got %s\n", wine_dbgstr_rect(&win_rc),
        wine_dbgstr_rect(&wp.rcNormalPosition));
 
diff --git a/dlls/user32/tests/win.c b/dlls/user32/tests/win.c
index 6ef13eaeaa3e..98030caa59cf 100644
--- a/dlls/user32/tests/win.c
+++ b/dlls/user32/tests/win.c
@@ -12696,9 +12696,59 @@ static void test_IsWindowEnabled(void)
     DestroyWindow(hwnd);
 }
 
+/* When a window is a top-level window that doesn't have WS_EX_TOOLWINDOW, ptMinPosition,
+ * ptMaxPosition, and rcNormalPosition are in work area coordinates. Otherwise, they are in screen
+ * coordinates. So we need to convert the coordinates to screen coordinates for correct comparison */
+static void placement_workarea_to_screen(HWND hwnd, WINDOWPLACEMENT *wp, const RECT *work_rect)
+{
+    DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
+    DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
+
+    if (style & WS_CHILD || ex_style & WS_EX_TOOLWINDOW)
+        return;
+
+    if (wp->ptMinPosition.x != -1 && wp->ptMinPosition.y != -1)
+    {
+        wp->ptMinPosition.x += work_rect->left;
+        wp->ptMinPosition.y += work_rect->top;
+    }
+
+    /* ptMaxPosition is in screen coordinates when WS_MAXIMIZE is present */
+    if (!(style & WS_MAXIMIZE) && wp->ptMaxPosition.x != -1 && wp->ptMaxPosition.y != -1)
+    {
+        wp->ptMaxPosition.x += work_rect->left;
+        wp->ptMaxPosition.y += work_rect->top;
+    }
+
+    OffsetRect(&wp->rcNormalPosition, work_rect->left, work_rect->top);
+}
+
+static void placement_screen_to_workarea(HWND hwnd, WINDOWPLACEMENT *wp, const RECT *work_rect)
+{
+    DWORD style = GetWindowLongW(hwnd, GWL_STYLE);
+    DWORD ex_style = GetWindowLongW(hwnd, GWL_EXSTYLE);
+
+    if (style & WS_CHILD || ex_style & WS_EX_TOOLWINDOW)
+        return;
+
+    if (wp->ptMinPosition.x != -1 && wp->ptMinPosition.y != -1)
+    {
+        wp->ptMinPosition.x -= work_rect->left;
+        wp->ptMinPosition.y -= work_rect->top;
+    }
+
+    if (wp->ptMaxPosition.x != -1 && wp->ptMaxPosition.y != -1)
+    {
+        wp->ptMaxPosition.x -= work_rect->left;
+        wp->ptMaxPosition.y -= work_rect->top;
+    }
+
+    OffsetRect(&wp->rcNormalPosition, -work_rect->left, -work_rect->top);
+}
+
 static void test_window_placement(void)
 {
-    RECT orig = {100, 200, 300, 400}, orig2 = {200, 300, 400, 500}, rect, work_rect;
+    RECT orig = {300, 400, 500, 600}, orig2 = {200, 300, 400, 500}, rect, work_rect;
     WINDOWPLACEMENT wp = {sizeof(wp)};
     MONITORINFO mon_info;
     HWND hwnd;
@@ -12714,6 +12764,7 @@ static void test_window_placement(void)
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWNORMAL, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -1 && wp.ptMinPosition.y == -1,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12726,6 +12777,7 @@ static void test_window_placement(void)
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(!wp.flags, "got flags %#x\n", wp.flags);
     ok(wp.showCmd == SW_SHOWMINIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
@@ -12739,6 +12791,7 @@ static void test_window_placement(void)
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWNORMAL, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12751,6 +12804,7 @@ static void test_window_placement(void)
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12759,14 +12813,15 @@ static void test_window_placement(void)
     ok(EqualRect(&wp.rcNormalPosition, &orig), "got normal pos %s\n",
         wine_dbgstr_rect(&wp.rcNormalPosition));
 
-    SetWindowPos(hwnd, 0, 100, 100, 100, 100, SWP_NOZORDER | SWP_NOACTIVATE);
+    SetWindowPos(hwnd, 0, 200, 200, 100, 100, SWP_NOZORDER | SWP_NOACTIVATE);
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
-    ok(wp.ptMaxPosition.x == 100 && wp.ptMaxPosition.y == 100,
+    ok(wp.ptMaxPosition.x == 200 && wp.ptMaxPosition.y == 200,
         "got maximized pos (%ld,%ld)\n", wp.ptMaxPosition.x, wp.ptMaxPosition.y);
     ok(EqualRect(&wp.rcNormalPosition, &orig), "got normal pos %s\n",
         wine_dbgstr_rect(&wp.rcNormalPosition));
@@ -12775,6 +12830,7 @@ static void test_window_placement(void)
                  work_rect.bottom - work_rect.top, SWP_NOZORDER | SWP_NOACTIVATE);
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12787,6 +12843,7 @@ static void test_window_placement(void)
                  work_rect.bottom - work_rect.top, SWP_NOZORDER | SWP_NOACTIVATE);
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12799,6 +12856,7 @@ static void test_window_placement(void)
                  work_rect.bottom - work_rect.top - 1, SWP_NOZORDER | SWP_NOACTIVATE);
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12807,10 +12865,23 @@ static void test_window_placement(void)
     ok(EqualRect(&wp.rcNormalPosition, &orig), "got normal pos %s\n",
         wine_dbgstr_rect(&wp.rcNormalPosition));
 
+    /* Test setting ptMaxPosition while the window is maximized */
+    wp.ptMaxPosition.x = 200;
+    wp.ptMaxPosition.y = 200;
+    placement_screen_to_workarea(hwnd, &wp, &work_rect);
+    ret = SetWindowPlacement(hwnd, &wp);
+    ok(ret, "failed to set window placement, error %lu\n", GetLastError());
+    ret = GetWindowPlacement(hwnd, &wp);
+    ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
+    ok(wp.ptMaxPosition.x == 200 && wp.ptMaxPosition.y == 200,
+        "got maximized pos (%ld,%ld)\n", wp.ptMaxPosition.x, wp.ptMaxPosition.y);
+
     ShowWindow(hwnd, SW_MINIMIZE);
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.flags == WPF_RESTORETOMAXIMIZED, "got flags %#x\n", wp.flags);
     ok(wp.showCmd == SW_SHOWMINIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
@@ -12824,6 +12895,7 @@ static void test_window_placement(void)
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12836,6 +12908,7 @@ static void test_window_placement(void)
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWNORMAL, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12845,16 +12918,18 @@ static void test_window_placement(void)
         wine_dbgstr_rect(&wp.rcNormalPosition));
 
     wp.flags = WPF_SETMINPOSITION;
-    wp.ptMinPosition.x = wp.ptMinPosition.y = 100;
-    wp.ptMaxPosition.x = wp.ptMaxPosition.y = 100;
+    wp.ptMinPosition.x = wp.ptMinPosition.y = 200;
+    wp.ptMaxPosition.x = wp.ptMaxPosition.y = 200;
     wp.rcNormalPosition = orig2;
+    placement_screen_to_workarea(hwnd, &wp, &work_rect);
     ret = SetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to set window placement, error %lu\n", GetLastError());
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWNORMAL, "got show cmd %u\n", wp.showCmd);
-    ok(wp.ptMinPosition.x == 100 && wp.ptMinPosition.y == 100,
+    ok(wp.ptMinPosition.x == 200 && wp.ptMinPosition.y == 200,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
     ok(wp.ptMaxPosition.x == -1 && wp.ptMaxPosition.y == -1,
         "got maximized pos (%ld,%ld)\n", wp.ptMaxPosition.x, wp.ptMaxPosition.y);
@@ -12867,6 +12942,7 @@ static void test_window_placement(void)
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(!wp.flags, "got flags %#x\n", wp.flags);
     ok(wp.showCmd == SW_SHOWMINIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
@@ -12880,14 +12956,16 @@ static void test_window_placement(void)
 
     wp.flags = WPF_SETMINPOSITION;
     wp.showCmd = SW_MINIMIZE;
-    wp.ptMinPosition.x = wp.ptMinPosition.y = 100;
-    wp.ptMaxPosition.x = wp.ptMaxPosition.y = 100;
+    wp.ptMinPosition.x = wp.ptMinPosition.y = 200;
+    wp.ptMaxPosition.x = wp.ptMaxPosition.y = 200;
     wp.rcNormalPosition = orig;
+    placement_screen_to_workarea(hwnd, &wp, &work_rect);
     ret = SetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to set window placement, error %lu\n", GetLastError());
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(!wp.flags, "got flags %#x\n", wp.flags);
     ok(wp.showCmd == SW_SHOWMINIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
@@ -12901,16 +12979,18 @@ static void test_window_placement(void)
 
     wp.flags = WPF_SETMINPOSITION;
     wp.showCmd = SW_MAXIMIZE;
-    wp.ptMinPosition.x = wp.ptMinPosition.y = 100;
-    wp.ptMaxPosition.x = wp.ptMaxPosition.y = 100;
+    wp.ptMinPosition.x = wp.ptMinPosition.y = 200;
+    wp.ptMaxPosition.x = wp.ptMaxPosition.y = 200;
     wp.rcNormalPosition = orig;
+    placement_screen_to_workarea(hwnd, &wp, &work_rect);
     ret = SetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to set window placement, error %lu\n", GetLastError());
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
-    ok(wp.ptMinPosition.x == 100 && wp.ptMinPosition.y == 100,
+    ok(wp.ptMinPosition.x == 200 && wp.ptMinPosition.y == 200,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
     ok(wp.ptMaxPosition.x == -1 && wp.ptMaxPosition.y == -1,
         "got maximized pos (%ld,%ld)\n", wp.ptMaxPosition.x, wp.ptMaxPosition.y);
@@ -12919,9 +12999,10 @@ static void test_window_placement(void)
 
     wp.flags = WPF_SETMINPOSITION;
     wp.showCmd = SW_NORMAL;
-    wp.ptMinPosition.x = wp.ptMinPosition.y = 100;
-    wp.ptMaxPosition.x = wp.ptMaxPosition.y = 100;
+    wp.ptMinPosition.x = wp.ptMinPosition.y = 200;
+    wp.ptMaxPosition.x = wp.ptMaxPosition.y = 200;
     wp.rcNormalPosition = orig;
+    placement_screen_to_workarea(hwnd, &wp, &work_rect);
     ret = SetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to set window placement, error %lu\n", GetLastError());
 
@@ -12929,6 +13010,7 @@ static void test_window_placement(void)
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMINIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12937,11 +13019,12 @@ static void test_window_placement(void)
     ok(EqualRect(&wp.rcNormalPosition, &orig), "got normal pos %s\n",
         wine_dbgstr_rect(&wp.rcNormalPosition));
 
-    ret = SetWindowPos(hwnd, NULL, 100, 100, 151, 151, SWP_NOACTIVATE | SWP_NOZORDER);
+    ret = SetWindowPos(hwnd, NULL, 200, 200, 151, 151, SWP_NOACTIVATE | SWP_NOZORDER);
     ok(ret, "failed to set window pos, error %lu\n", GetLastError());
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMINIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12956,6 +13039,7 @@ static void test_window_placement(void)
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_NORMAL, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -32000 && wp.ptMinPosition.y == -32000,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12966,6 +13050,7 @@ static void test_window_placement(void)
     GetWindowRect(hwnd, &rect);
     ok(EqualRect(&rect, &orig), "got window rect %s\n", wine_dbgstr_rect(&rect));
 
+    placement_screen_to_workarea(hwnd, &wp, &work_rect);
     ret = SetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to set window placement, error %lu\n", GetLastError());
 
@@ -12991,6 +13076,7 @@ static void test_window_placement(void)
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -1 && wp.ptMinPosition.y == -1,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -12999,14 +13085,15 @@ static void test_window_placement(void)
     ok(EqualRect(&wp.rcNormalPosition, &orig), "got normal pos %s\n",
         wine_dbgstr_rect(&wp.rcNormalPosition));
 
-    SetWindowPos(hwnd, 0, 100, 100, 100, 100, SWP_NOZORDER | SWP_NOACTIVATE);
+    SetWindowPos(hwnd, 0, 200, 200, 100, 100, SWP_NOZORDER | SWP_NOACTIVATE);
 
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -1 && wp.ptMinPosition.y == -1,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
-    ok(wp.ptMaxPosition.x == 100 && wp.ptMaxPosition.y == 100,
+    ok(wp.ptMaxPosition.x == 200 && wp.ptMaxPosition.y == 200,
         "got maximized pos (%ld,%ld)\n", wp.ptMaxPosition.x, wp.ptMaxPosition.y);
     ok(EqualRect(&wp.rcNormalPosition, &orig), "got normal pos %s\n",
         wine_dbgstr_rect(&wp.rcNormalPosition));
@@ -13015,6 +13102,7 @@ static void test_window_placement(void)
         work_rect.bottom - work_rect.top, SWP_NOZORDER | SWP_NOACTIVATE);
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -1 && wp.ptMinPosition.y == -1,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -13027,6 +13115,7 @@ static void test_window_placement(void)
         work_rect.bottom - work_rect.top, SWP_NOZORDER | SWP_NOACTIVATE);
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -1 && wp.ptMinPosition.y == -1,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -13039,6 +13128,7 @@ static void test_window_placement(void)
         work_rect.bottom - work_rect.top - 1, SWP_NOZORDER | SWP_NOACTIVATE);
     ret = GetWindowPlacement(hwnd, &wp);
     ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
     ok(wp.showCmd == SW_SHOWMAXIMIZED, "got show cmd %u\n", wp.showCmd);
     ok(wp.ptMinPosition.x == -1 && wp.ptMinPosition.y == -1,
         "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
@@ -13048,6 +13138,82 @@ static void test_window_placement(void)
         wine_dbgstr_rect(&wp.rcNormalPosition));
 
     DestroyWindow(hwnd);
+
+    /* Test windows with WS_CHILD */
+    hwnd = CreateWindowA("MainWindowClass", "wp", WS_CHILD, orig.left, orig.top,
+        orig.right - orig.left, orig.bottom - orig.top, hwndMain, 0, 0, 0);
+    ok(!!hwnd, "failed to create window, error %lu\n", GetLastError());
+
+    ret = GetWindowPlacement(hwnd, &wp);
+    ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
+    ok(wp.showCmd == SW_SHOWNORMAL, "got show cmd %u\n", wp.showCmd);
+    ok(wp.ptMinPosition.x == -1 && wp.ptMinPosition.y == -1,
+        "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
+    ok(wp.ptMaxPosition.x == -1 && wp.ptMaxPosition.y == -1,
+        "got maximized pos (%ld,%ld)\n", wp.ptMaxPosition.x, wp.ptMaxPosition.y);
+    ok(EqualRect(&wp.rcNormalPosition, &orig), "got normal pos %s\n",
+        wine_dbgstr_rect(&wp.rcNormalPosition));
+
+    wp.flags = WPF_SETMINPOSITION;
+    wp.ptMinPosition.x = wp.ptMinPosition.y = 200;
+    wp.ptMaxPosition.x = wp.ptMaxPosition.y = 200;
+    wp.rcNormalPosition = orig2;
+    placement_screen_to_workarea(hwnd, &wp, &work_rect);
+    ret = SetWindowPlacement(hwnd, &wp);
+    ok(ret, "failed to set window placement, error %lu\n", GetLastError());
+
+    ret = GetWindowPlacement(hwnd, &wp);
+    ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
+    ok(wp.showCmd == SW_SHOWNORMAL, "got show cmd %u\n", wp.showCmd);
+    ok(wp.ptMinPosition.x == 200 && wp.ptMinPosition.y == 200,
+        "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
+    ok(wp.ptMaxPosition.x == 200 && wp.ptMaxPosition.y == 200,
+        "got maximized pos (%ld,%ld)\n", wp.ptMaxPosition.x, wp.ptMaxPosition.y);
+    ok(EqualRect(&wp.rcNormalPosition, &orig2), "got normal pos %s\n",
+        wine_dbgstr_rect(&wp.rcNormalPosition));
+
+    DestroyWindow(hwnd);
+
+    /* Test windows with WS_EX_TOOLWINDOW */
+    hwnd = CreateWindowExA(WS_EX_TOOLWINDOW, "MainWindowClass", "wp", WS_OVERLAPPEDWINDOW,
+        orig.left, orig.top, orig.right - orig.left, orig.bottom - orig.top, 0, 0, 0, 0);
+    ok(!!hwnd, "failed to create window, error %lu\n", GetLastError());
+
+    ret = GetWindowPlacement(hwnd, &wp);
+    ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
+    ok(wp.showCmd == SW_SHOWNORMAL, "got show cmd %u\n", wp.showCmd);
+    ok(wp.ptMinPosition.x == -1 && wp.ptMinPosition.y == -1,
+        "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
+    ok(wp.ptMaxPosition.x == -1 && wp.ptMaxPosition.y == -1,
+        "got maximized pos (%ld,%ld)\n", wp.ptMaxPosition.x, wp.ptMaxPosition.y);
+    ok(EqualRect(&wp.rcNormalPosition, &orig), "got normal pos %s\n",
+        wine_dbgstr_rect(&wp.rcNormalPosition));
+
+    wp.flags = WPF_SETMINPOSITION;
+    wp.ptMinPosition.x = wp.ptMinPosition.y = 200;
+    wp.ptMaxPosition.x = wp.ptMaxPosition.y = 200;
+    wp.rcNormalPosition = orig2;
+    placement_screen_to_workarea(hwnd, &wp, &work_rect);
+    ret = SetWindowPlacement(hwnd, &wp);
+    ok(ret, "failed to set window placement, error %lu\n", GetLastError());
+
+    ret = GetWindowPlacement(hwnd, &wp);
+    ok(ret, "failed to get window placement, error %lu\n", GetLastError());
+    placement_workarea_to_screen(hwnd, &wp, &work_rect);
+    ok(wp.showCmd == SW_SHOWNORMAL, "got show cmd %u\n", wp.showCmd);
+    ok(wp.ptMinPosition.x == 200 && wp.ptMinPosition.y == 200,
+        "got minimized pos (%ld,%ld)\n", wp.ptMinPosition.x, wp.ptMinPosition.y);
+    ok(wp.ptMaxPosition.x == -1 && wp.ptMaxPosition.y == -1,
+        "got maximized pos (%ld,%ld)\n", wp.ptMaxPosition.x, wp.ptMaxPosition.y);
+    ok(EqualRect(&wp.rcNormalPosition, &orig2), "got normal pos %s\n",
+        wine_dbgstr_rect(&wp.rcNormalPosition));
+    GetWindowRect(hwnd, &rect);
+    ok(EqualRect(&rect, &orig2), "got window rect %s\n", wine_dbgstr_rect(&rect));
+
+    DestroyWindow(hwnd);
 }
 
 static void test_arrange_iconic_windows(void)
diff --git a/dlls/user32/text.c b/dlls/user32/text.c
index b757109c9b53..114d0695ac82 100644
--- a/dlls/user32/text.c
+++ b/dlls/user32/text.c
@@ -247,7 +247,7 @@ static void TEXT_PathEllipsify (HDC hdc, WCHAR *str, unsigned int max_len,
     lastBkSlash  = wcsrchr (str, BACK_SLASH);
     lastFwdSlash = wcsrchr (str, FORWARD_SLASH);
     lastSlash = lastBkSlash > lastFwdSlash ? lastBkSlash : lastFwdSlash;
-    if (!lastSlash) lastSlash = str;
+    if (!lastSlash) lastSlash = str + *len_str;
     len_trailing = *len_str - (lastSlash - str);
 
     /* overlap-safe movement to the right */
diff --git a/dlls/user32/user32.spec b/dlls/user32/user32.spec
index e60331864902..241b40054b8d 100644
--- a/dlls/user32/user32.spec
+++ b/dlls/user32/user32.spec
@@ -632,21 +632,21 @@
 @ stdcall GetPointerDeviceProperties(ptr ptr ptr)
 @ stdcall GetPointerDeviceRects(ptr ptr ptr) NtUserGetPointerDeviceRects
 @ stdcall GetPointerDevices(ptr ptr)
-# @ stub GetPointerFrameInfo
-# @ stub GetPointerFrameInfoHistory
-# @ stub GetPointerFramePenInfo
-# @ stub GetPointerFramePenInfoHistory
+@ stdcall GetPointerFrameInfo(long ptr ptr)
+@ stdcall GetPointerFrameInfoHistory(long ptr ptr ptr)
+@ stdcall GetPointerFramePenInfo(long ptr ptr)
+@ stdcall GetPointerFramePenInfoHistory(long ptr ptr ptr)
 # @ stub GetPointerFrameTimes
-# @ stub GetPointerFrameTouchInfo
-# @ stub GetPointerFrameTouchInfoHistory
+@ stdcall GetPointerFrameTouchInfo(long ptr ptr)
+@ stdcall GetPointerFrameTouchInfoHistory(long ptr ptr ptr)
 @ stdcall GetPointerInfo(long ptr)
-# @ stub GetPointerInfoHistory
+@ stdcall GetPointerInfoHistory(long ptr ptr)
 # @ stub GetPointerInputTransform
 @ stdcall GetPointerPenInfo(long ptr)
-# @ stub GetPointerPenInfoHistory
+@ stdcall GetPointerPenInfoHistory(long ptr ptr)
 @ stdcall GetPointerTouchInfo(long ptr)
 @ stdcall GetPointerTouchInfoHistory(long ptr ptr)
-@ stdcall GetPointerType(long ptr)
+@ stdcall GetPointerType(long ptr) NtUserGetPointerType
 @ stdcall GetPriorityClipboardFormat(ptr long) NtUserGetPriorityClipboardFormat
 @ stdcall GetProcessDefaultLayout(ptr)
 @ stdcall GetProcessDpiAwarenessInternal(long ptr)
diff --git a/dlls/vbscript/tests/run.c b/dlls/vbscript/tests/run.c
index 20a835f77025..174d86ac3328 100644
--- a/dlls/vbscript/tests/run.c
+++ b/dlls/vbscript/tests/run.c
@@ -3782,6 +3782,41 @@ static void test_named_item_builtin_shadowing(void)
     close_script(engine);
 }
 
+/* Native counts '\n', '\r' and '\r\n' as line endings, so a '\n\r' pair is
+   two. Each script ends with a failing statement preceded by two separators,
+   and the reported 0-based error line is the number of line endings before
+   it: 2 for the single-break styles, 4 for the doubled '\n\r' and '\r\r'. */
+static void test_error_line_endings(void)
+{
+    static const struct {
+        const WCHAR *src;
+        ULONG error_line;
+    } tests[] = {
+        { L"' a\n' b\nx = 1 \\ 0\n",             2 },
+        { L"' a\r\n' b\r\nx = 1 \\ 0\r\n",       2 },
+        { L"' a\r' b\rx = 1 \\ 0\r",             2 },
+        { L"' a\n' b\rx = 1 \\ 0\r",             2 },
+        { L"' a\r' b\nx = 1 \\ 0\n",             2 },
+        { L"' a\n\r' b\n\rx = 1 \\ 0\n\r",       4 },
+        { L"' a\r\r' b\r\rx = 1 \\ 0\r\r",       4 },
+        { L"' a\r\n' b\rx = 1 \\ 0\r\n",         2 },
+    };
+    HRESULT hres;
+    unsigned i;
+
+    for (i = 0; i < ARRAY_SIZE(tests); i++) {
+        error_line = ~0;
+        error_code = 0;
+        onerror_hres = S_OK;
+        SET_EXPECT(OnScriptError);
+        hres = parse_script_wr(tests[i].src);
+        CLEAR_CALLED(OnScriptError);
+        ok(hres == 0x80020101 && error_code == 11 && error_line == tests[i].error_line,
+           "[%u] %s: hres=%08lx code=%u line=%lu\n", i, wine_dbgstr_w(tests[i].src),
+           hres, error_code, error_line);
+    }
+}
+
 static void test_msgbox(void)
 {
     HRESULT hres;
@@ -4487,6 +4522,7 @@ static void run_tests(void)
     test_parse_errors();
     test_class_decl_scope();
     test_sub_decl_scope();
+    test_error_line_endings();
     test_redefine_scope();
     test_getref_error_reporting();
     test_getref_external_caller_error();
diff --git a/dlls/vbscript/vbscript.c b/dlls/vbscript/vbscript.c
index a4cfeea9eab4..9a42f7009bd7 100644
--- a/dlls/vbscript/vbscript.c
+++ b/dlls/vbscript/vbscript.c
@@ -688,7 +688,10 @@ HRESULT report_script_error(script_ctx_t *ctx, vbscode_t *code, unsigned loc, BO
     error->cookie = code->cookie;
     error->line = code->start_line;
     for(nl = p = code->source; p < code->source + loc; p++) {
-        if(*p != '\n') continue;
+        /* A bare '\r' (classic Mac line ending) starts a new line just like
+           '\n'; a '\r\n' pair counts as a single line, tallied on the '\n'. */
+        if(*p != '\n' && (*p != '\r' || p[1] == '\n'))
+            continue;
         error->line++;
         nl = p + 1;
     }
diff --git a/dlls/win32u/clipping.c b/dlls/win32u/clipping.c
index cf71cf0645c0..60e8ee6ba165 100644
--- a/dlls/win32u/clipping.c
+++ b/dlls/win32u/clipping.c
@@ -463,7 +463,7 @@ INT WINAPI NtGdiGetRandomRgn( HDC hDC, HRGN hRgn, INT iCode )
     if (ret > 0 && (iCode & NTGDI_RGN_MONITOR_DPI))
     {
         HWND hwnd = NtUserWindowFromDC( hDC );
-        UINT raw_dpi;
+        struct ratio raw_dpi;
         HRGN region;
 
         get_win_monitor_dpi( hwnd, &raw_dpi );
diff --git a/dlls/win32u/d3dkmt.c b/dlls/win32u/d3dkmt.c
index 1b33976f333e..9b5d4a8c9197 100644
--- a/dlls/win32u/d3dkmt.c
+++ b/dlls/win32u/d3dkmt.c
@@ -550,7 +550,7 @@ NTSTATUS WINAPI NtGdiDdDDIEscape( const D3DKMT_ESCAPE *desc )
     {
         HWND hwnd = UlongToHandle( desc->hContext );
         RECT *rect = desc->pPrivateDriverData;
-        UINT dpi = get_dpi_for_window( hwnd );
+        struct ratio dpi = get_dpi_for_window( hwnd );
         WND *win;
 
         if (desc->PrivateDriverDataSize != sizeof(*rect)) return STATUS_INVALID_PARAMETER;
diff --git a/dlls/win32u/dce.c b/dlls/win32u/dce.c
index ad27cb143ae2..5e8d82aed341 100644
--- a/dlls/win32u/dce.c
+++ b/dlls/win32u/dce.c
@@ -144,8 +144,8 @@ struct scaled_surface
 {
     struct window_surface header;
     struct window_surface *target_surface;
-    UINT dpi_from;
-    UINT dpi_to;
+    struct ratio dpi_from;
+    struct ratio dpi_to;
 };
 
 static struct scaled_surface *get_scaled_surface( struct window_surface *window_surface )
@@ -223,14 +223,14 @@ static const struct window_surface_funcs scaled_surface_funcs =
     scaled_surface_destroy
 };
 
-static void scaled_surface_set_target( struct scaled_surface *surface, struct window_surface *target, UINT dpi_to )
+static void scaled_surface_set_target( struct scaled_surface *surface, struct window_surface *target, struct ratio dpi_to )
 {
     if (surface->target_surface) window_surface_release( surface->target_surface );
     window_surface_add_ref( (surface->target_surface = target) );
     surface->dpi_to = dpi_to;
 }
 
-static struct window_surface *scaled_surface_create( HWND hwnd, const RECT *surface_rect, UINT dpi_from, UINT dpi_to,
+static struct window_surface *scaled_surface_create( HWND hwnd, const RECT *surface_rect, struct ratio dpi_from, struct ratio dpi_to,
                                                      struct window_surface *target_surface )
 {
     char buffer[FIELD_OFFSET( BITMAPINFO, bmiColors[256] )];
@@ -269,11 +269,11 @@ static RECT get_surface_rect( RECT rect )
     return rect;
 }
 
-void create_window_surface( HWND hwnd, BOOL create_layered, const RECT *surface_rect, UINT monitor_dpi,
+void create_window_surface( HWND hwnd, BOOL create_layered, const RECT *surface_rect, struct ratio monitor_dpi,
                             struct window_surface **window_surface )
 {
     struct window_surface *previous, *driver_surface;
-    UINT dpi = get_dpi_for_window( hwnd );
+    struct ratio dpi = get_dpi_for_window( hwnd );
     RECT monitor_rect;
 
 
@@ -296,7 +296,7 @@ void create_window_surface( HWND hwnd, BOOL create_layered, const RECT *surface_
         return;
     }
 
-    if (!driver_surface || dpi == monitor_dpi)
+    if (!driver_surface || !memcmp( &dpi, &monitor_dpi, sizeof(monitor_dpi) ))
     {
         if (*window_surface) window_surface_release( *window_surface );
         *window_surface = driver_surface;
@@ -317,11 +317,11 @@ void create_window_surface( HWND hwnd, BOOL create_layered, const RECT *surface_
     window_surface_release( driver_surface );
 }
 
-struct window_surface *get_driver_window_surface( struct window_surface *surface, UINT monitor_dpi )
+struct window_surface *get_driver_window_surface( struct window_surface *surface, struct ratio monitor_dpi )
 {
     if (!surface || surface == &dummy_surface) return surface;
     if (surface->funcs != &scaled_surface_funcs) return surface;
-    if (get_scaled_surface( surface )->dpi_to != monitor_dpi) return &dummy_surface;
+    if (memcmp( &get_scaled_surface( surface )->dpi_to, &monitor_dpi, sizeof(monitor_dpi) )) return &dummy_surface;
     return get_scaled_surface( surface )->target_surface;
 }
 
@@ -1936,7 +1936,7 @@ BOOL WINAPI NtUserGetUpdateRect( HWND hwnd, RECT *rect, BOOL erase )
     {
         HDC hdc = NtUserGetDCEx( hwnd, 0, DCX_USESTYLE );
         DWORD layout = NtGdiSetLayout( hdc, -1, 0 );  /* map_window_points mirrors already */
-        UINT win_dpi = get_dpi_for_window( hwnd );
+        struct ratio win_dpi = get_dpi_for_window( hwnd );
         map_window_points( 0, hwnd, (POINT *)rect, 2, win_dpi );
         *rect = map_dpi_rect( *rect, win_dpi, get_thread_dpi() );
         NtGdiTransformPoints( hdc, (POINT *)rect, (POINT *)rect, 2, NtGdiDPtoLP );
diff --git a/dlls/win32u/defwnd.c b/dlls/win32u/defwnd.c
index 5951374fc9a8..d6852b33ce2c 100644
--- a/dlls/win32u/defwnd.c
+++ b/dlls/win32u/defwnd.c
@@ -37,6 +37,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(win);
 #define KEYDATA_ALT             0x2000
 #define KEYDATA_PREVSTATE       0x4000
 
+static const struct ratio no_dpi;
 static short f10_key = 0;
 static short menu_sys_key = 0;
 
@@ -682,8 +683,8 @@ static void sys_command_size_move( HWND hwnd, WPARAM wparam )
     UINT style = get_window_long( hwnd, GWL_STYLE );
     POINT capture_point, pt;
     MINMAXINFO minmax;
+    struct ratio dpi;
     HWND parent;
-    UINT dpi;
     HDC hdc;
     MSG msg;
 
@@ -975,7 +976,7 @@ static LRESULT handle_sys_command( HWND hwnd, WPARAM wparam, LPARAM lparam )
     pos.y = (short)HIWORD( msgpos );
     NtUserLogicalToPerMonitorDPIPhysicalPoint( hwnd, &pos );
     SetRect( &rect, pos.x, pos.y, pos.x, pos.y );
-    rect = map_rect_virt_to_raw( rect, 0 );
+    rect = map_rect_virt_to_raw( rect, no_dpi );
     pos.x = rect.left;
     pos.y = rect.top;
 
@@ -1477,7 +1478,7 @@ static void draw_close_button( HWND hwnd, HDC hdc, BOOL down, BOOL grayed )
     {
         /* Windows does not use SM_CXSMSIZE and SM_CYSMSIZE
          * it uses 11x11 for  the close button in tool window */
-        int bmp_height = muldiv( 11, get_dpi_for_window( hwnd ), 96 );
+        int bmp_height = map_user_dpi( 11, get_dpi_for_window( hwnd ) );
         int bmp_width = bmp_height;
         int caption_height = get_system_metrics( SM_CYSMCAPTION );
 
@@ -3038,7 +3039,7 @@ LRESULT desktop_window_proc( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam,
     {
         static RECT virtual_rect = {INT_MIN,INT_MIN,INT_MAX,INT_MAX};
 
-        RECT new_rect = get_virtual_screen_rect( 0, MDT_DEFAULT ), old_rect = virtual_rect;
+        RECT new_rect = get_virtual_screen_rect( no_dpi, MDT_DEFAULT ), old_rect = virtual_rect;
         UINT context, flags = 0;
 
         if (EqualRect( &new_rect, &old_rect )) return TRUE;
diff --git a/dlls/win32u/driver.c b/dlls/win32u/driver.c
index 27259fd85063..6270d9bbe10f 100644
--- a/dlls/win32u/driver.c
+++ b/dlls/win32u/driver.c
@@ -36,6 +36,7 @@
 WINE_DEFAULT_DEBUG_CHANNEL(driver);
 WINE_DECLARE_DEBUG_CHANNEL(winediag);
 
+static const struct ratio no_dpi;
 static const struct user_driver_funcs lazy_load_driver;
 static struct user_driver_funcs null_user_driver;
 static WCHAR driver_load_error[80];
@@ -252,14 +253,14 @@ static INT nulldrv_GetDeviceCaps( PHYSDEV dev, INT cap )
     case DESKTOPHORZRES:
         if (NtGdiGetDeviceCaps( dev->hdc, TECHNOLOGY ) == DT_RASDISPLAY)
         {
-            RECT rect = get_virtual_screen_rect( 0, MDT_DEFAULT );
+            RECT rect = get_virtual_screen_rect( no_dpi, MDT_DEFAULT );
             return rect.right - rect.left;
         }
         return NtGdiGetDeviceCaps( dev->hdc, HORZRES );
     case DESKTOPVERTRES:
         if (NtGdiGetDeviceCaps( dev->hdc, TECHNOLOGY ) == DT_RASDISPLAY)
         {
-            RECT rect = get_virtual_screen_rect( 0, MDT_DEFAULT );
+            RECT rect = get_virtual_screen_rect( no_dpi, MDT_DEFAULT );
             return rect.bottom - rect.top;
         }
         return NtGdiGetDeviceCaps( dev->hdc, VERTRES );
diff --git a/dlls/win32u/input.c b/dlls/win32u/input.c
index ce7d0d4de580..32cb925b6012 100644
--- a/dlls/win32u/input.c
+++ b/dlls/win32u/input.c
@@ -42,6 +42,8 @@ WINE_DECLARE_DEBUG_CHANNEL(keyboard);
 
 #define HIMETRIC_PER_INCH 2540
 
+static const struct ratio no_dpi;
+
 static const WCHAR keyboard_layouts_keyW[] =
 {
     '\\','R','e','g','i','s','t','r','y',
@@ -407,6 +409,15 @@ static const KBDTABLES kbdus_tables =
 
 static LONG clipping_cursor; /* clipping thread counter */
 static LONG enable_mouse_in_pointer = -1;
+static LONG last_frame = 0;
+
+struct pointer
+{
+    UINT32 id;
+    struct list entry;
+    POINTER_INPUT_TYPE type;
+    POINTER_INFO info;
+};
 
 BOOL grab_pointer = TRUE;
 BOOL grab_fullscreen = FALSE;
@@ -621,9 +632,9 @@ static void update_mouse_coords( INPUT *input )
         RECT rc;
 
         if (input->mi.dwFlags & MOUSEEVENTF_VIRTUALDESK)
-            rc = get_virtual_screen_rect( 0, MDT_DEFAULT );
+            rc = get_virtual_screen_rect( no_dpi, MDT_DEFAULT );
         else
-            rc = get_primary_monitor_rect( 0 );
+            rc = get_primary_monitor_rect( no_dpi );
 
         input->mi.dx = rc.left + ((input->mi.dx * (rc.right - rc.left)) >> 16);
         input->mi.dy = rc.top  + ((input->mi.dy * (rc.bottom - rc.top)) >> 16);
@@ -650,6 +661,71 @@ static void update_mouse_coords( INPUT *input )
     }
 }
 
+static const char *debugstr_mouseinput( const MOUSEINPUT *mi )
+{
+    return wine_dbg_sprintf( "pos %s, flags %#x, data %#x, time %d", wine_dbgstr_point( (POINT *)&mi->dx ),
+                             mi->dwFlags, mi->mouseData, mi->time );
+}
+
+static NTSTATUS send_mouse_motion( UINT flags )
+{
+    struct user_thread_info *info = get_user_thread_info();
+    INPUT input = info->mouse_motion;
+    NTSTATUS status;
+
+    if (!input.mi.dwFlags && !input.mi.mouseData) return STATUS_SUCCESS; /* ignore empty inputs */
+
+    TRACE( "Sending %s (%u raw frames)\n", debugstr_mouseinput( &input.mi ), info->raw_mouse.count );
+    status = server_send_hardware_message( info->mouse_hwnd, flags, &input, (LPARAM)&info->raw_mouse );
+    memset( &info->mouse_motion, 0, sizeof(info->mouse_motion) );
+    info->raw_mouse.count = 0;
+    info->mouse_hwnd = NULL;
+
+    return status;
+}
+
+static NTSTATUS accum_mouse_motion( HWND hwnd, UINT flags, INPUT input, const struct raw_mouse *raw )
+{
+    struct user_thread_info *info = get_user_thread_info();
+    BOOL send;
+
+    /* don't accumulate if there's button / wheel / MOUSEEVENTF_MOVE_NOCOALESCE */
+    send = input.mi.mouseData || (input.mi.dwFlags & ~(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE));
+    if (send || info->mouse_hwnd != hwnd) send_mouse_motion( flags );
+    else if (info->raw_mouse.count + raw->count > ARRAY_SIZE(raw->data)) send_mouse_motion( flags );
+
+    input.mi.dwFlags &= ~MOUSEEVENTF_MOVE_NOCOALESCE;
+    if (!input.mi.dwFlags) input = info->mouse_motion; /* only MOUSEEVENTF_MOVE_NOCOALESCE or raw */
+    else if (!(input.mi.dwFlags & MOUSEEVENTF_ABSOLUTE))
+    {
+        input.mi.dwFlags |= (info->mouse_motion.mi.dwFlags & MOUSEEVENTF_ABSOLUTE);
+        input.mi.dx += info->mouse_motion.mi.dx;
+        input.mi.dy += info->mouse_motion.mi.dy;
+    }
+
+    info->mouse_hwnd = hwnd;
+    info->mouse_motion = input;
+    memcpy( info->raw_mouse.data + info->raw_mouse.count, raw->data, raw->count * sizeof(*raw->data) );
+    info->raw_mouse.count += raw->count;
+
+    if (send) return send_mouse_motion( flags );
+
+    TRACE( "Keeping %s (%u raw frames)\n", debugstr_mouseinput( &info->mouse_motion.mi ), info->raw_mouse.count );
+    return STATUS_SUCCESS;
+}
+
+/* NtUserSendHardwareInput, for user drivers to feed host device input to wineserver */
+NTSTATUS send_hardware_input( HWND hwnd, UINT flags, const INPUT *input, LPARAM lparam )
+{
+    if (input->type == INPUT_MOUSE)
+    {
+        const struct raw_mouse empty_raw = {0}, *raw = lparam ? (void *)lparam : &empty_raw;
+        return accum_mouse_motion( hwnd, flags, *input, raw );
+    }
+
+    return server_send_hardware_message( hwnd, flags, input, lparam );
+}
+
 /***********************************************************************
  *           NtUserSendInput  (win32u.@)
  */
@@ -686,7 +762,7 @@ UINT WINAPI NtUserSendInput( UINT count, INPUT *inputs, int size )
             update_mouse_coords( &input );
             /* fallthrough */
         case INPUT_KEYBOARD:
-            status = send_hardware_message( 0, SEND_HWMSG_INJECTED, &input, 0 );
+            status = server_send_hardware_message( 0, SEND_HWMSG_INJECTED, &input, 0 );
             break;
         case INPUT_HARDWARE:
             RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED );
@@ -1217,13 +1293,13 @@ UINT WINAPI NtUserMapVirtualKeyEx( UINT code, UINT type, HKL layout )
  *
  * Map a scancode to a virtual key with KBD information.
  */
-USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout )
+USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout, UINT *mapped )
 {
     const KBDTABLES *kbd_tables;
     USHORT vsc2vk[0x300];
     UINT vkey;
 
-    if ((vkey = user_driver->pMapVirtualKeyEx( scan, MAPVK_VSC_TO_VK_EX, layout )) != -1) return vkey;
+    if ((vkey = user_driver->pMapVirtualKeyEx( scan, MAPVK_VSC_TO_VK_EX, layout )) != -1) goto done;
 
     if (!(kbd_tables = user_driver->pKbdLayerDescriptor( layout ))) kbd_tables = &kbdus_tables;
 
@@ -1234,6 +1310,15 @@ USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout )
 
     if (kbd_tables != &kbdus_tables) user_driver->pReleaseKbdTables( kbd_tables );
 
+    /* remap some scancodes as native does */
+done:
+    switch (vkey & 0xff)
+    {
+    case VK_PAUSE: *mapped = 0x45; break;
+    case VK_RSHIFT: *mapped = 0x136; break;
+    case VK_NUMLOCK: *mapped = 0x145; break;
+    default: *mapped = scan; break;
+    }
     return vkey;
 }
 
@@ -2480,7 +2565,7 @@ BOOL set_ime_composition_rect( HWND hwnd, RECT rect )
 {
     if (!NtUserIsWindow( hwnd )) return FALSE;
     NtUserMapWindowPoints( hwnd, 0, (POINT *)&rect, 2, 0 /* per-monitor DPI */ );
-    rect = map_rect_virt_to_raw( rect, 0 /* per-monitor DPI */ );
+    rect = map_rect_virt_to_raw( rect, no_dpi /* per-monitor DPI */ );
     return user_driver->pSetIMECompositionRect( NtUserGetAncestor( hwnd, GA_ROOT ), rect );
 }
 
@@ -2736,19 +2821,7 @@ BOOL clip_fullscreen_window( HWND hwnd, BOOL reset )
     return ret;
 }
 
-/**********************************************************************
- *       NtUserGetPointerInfoList    (win32u.@)
- */
-BOOL WINAPI NtUserGetPointerInfoList( UINT32 id, POINTER_INPUT_TYPE type, UINT_PTR unk0, UINT_PTR unk1, SIZE_T size,
-                                      UINT32 *entry_count, UINT32 *pointer_count, void *pointer_info )
-{
-    FIXME( "id %#x, type %#x, unk0 %#lx, unk1 %#lx, size %#lx, entry_count %p, pointer_count %p, pointer_info %p stub!\n",
-           id, type, (long)unk0, (long)unk1, size, entry_count, pointer_count, pointer_info );
-    RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED );
-    return FALSE;
-}
-
-static BOOL get_clip_cursor( RECT *rect, UINT dpi, MONITOR_DPI_TYPE type )
+static BOOL get_clip_cursor( RECT *rect, struct ratio dpi, MONITOR_DPI_TYPE type )
 {
     struct object_lock lock = OBJECT_LOCK_INIT;
     const desktop_shm_t *desktop_shm;
@@ -2766,12 +2839,12 @@ static BOOL get_clip_cursor( RECT *rect, UINT dpi, MONITOR_DPI_TYPE type )
 BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset )
 {
     struct user_thread_info *thread_info = get_user_thread_info();
-    RECT rect, virtual_rect = get_virtual_screen_rect( 0, MDT_RAW_DPI );
-    BOOL was_clipping, empty = !!(flags & SET_CURSOR_NOCLIP);
+    RECT rect, virtual_rect = get_virtual_screen_rect( no_dpi, MDT_RAW_DPI );
+    BOOL empty = !!(flags & SET_CURSOR_NOCLIP);
 
     TRACE( "hwnd %p, flags %#x, reset %u\n", hwnd, flags, reset );
 
-    if ((was_clipping = thread_info->clipping_cursor)) InterlockedDecrement( &clipping_cursor );
+    if (thread_info->clipping_cursor) InterlockedDecrement( &clipping_cursor );
     thread_info->clipping_cursor = FALSE;
 
     if (reset)
@@ -2783,13 +2856,13 @@ BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset )
     if (!grab_pointer) return TRUE;
 
     /* we are clipping if the clip rectangle is smaller than the screen */
-    get_clip_cursor( &rect, 0, MDT_RAW_DPI );
+    get_clip_cursor( &rect, no_dpi, MDT_RAW_DPI );
     intersect_rect( &rect, &rect, &virtual_rect );
     if (EqualRect( &rect, &virtual_rect )) empty = TRUE;
     if (empty && !(flags & SET_CURSOR_FSCLIP))
     {
-        /* if currently clipping, check if we should switch to fullscreen clipping */
-        if (was_clipping && clip_fullscreen_window( hwnd, TRUE )) return TRUE;
+        /* check if we should switch to fullscreen clipping */
+        if (clip_fullscreen_window( hwnd, TRUE )) return TRUE;
         return user_driver->pClipCursor( NULL, FALSE );
     }
 
@@ -2812,7 +2885,7 @@ BOOL WINAPI NtUserGetClipCursor( RECT *rect )
  */
 BOOL WINAPI NtUserClipCursor( const RECT *rect )
 {
-    UINT dpi = get_thread_dpi();
+    struct ratio dpi = get_thread_dpi();
     RECT new_rect;
     BOOL ret;
 
@@ -2870,6 +2943,145 @@ INT WINAPI NtUserScheduleDispatchNotification( HWND hwnd )
     return 0;
 }
 
+void destroy_thread_pointers(void)
+{
+    struct user_thread_info *thread_info = get_user_thread_info();
+    struct pointer *pointer, *next;
+
+    LIST_FOR_EACH_ENTRY_SAFE( pointer, next, &thread_info->known_pointers, struct pointer, entry )
+    {
+        list_remove( &pointer->entry );
+        free( pointer );
+    }
+}
+
+static struct pointer *pointer_create( UINT32 id, POINTER_INPUT_TYPE type )
+{
+    struct user_thread_info *thread_info = get_user_thread_info();
+    struct pointer *pointer;
+
+    TRACE( "allocating pointer id %d, type %#x\n", id, type );
+
+    if (!(pointer = calloc( 1, sizeof(*pointer) ))) return NULL;
+    pointer->id = id;
+    pointer->type = type;
+    list_add_tail( &thread_info->known_pointers, &pointer->entry );
+
+    return pointer;
+}
+
+static struct pointer *find_pointer( UINT32 id )
+{
+    struct user_thread_info *thread_info = get_user_thread_info();
+    struct pointer *pointer;
+
+    TRACE( "looking for pointer id %d\n", id );
+
+    LIST_FOR_EACH_ENTRY( pointer, &thread_info->known_pointers, struct pointer, entry )
+        if (pointer->id == id) return pointer;
+
+    WARN( "failed to find pointer with id %d\n", id );
+    return NULL;
+}
+
+static POINTER_INFO pointer_info_from_msg( const MSG *msg )
+{
+    POINT location = { LOWORD( msg->lParam ), HIWORD( msg->lParam ) };
+    LARGE_INTEGER counter;
+    POINTER_INFO info =
+    {
+        .pointerId = GET_POINTERID_WPARAM( msg->wParam ),
+        .sourceDevice = INVALID_HANDLE_VALUE,
+        .frameId = InterlockedIncrement( &last_frame ),
+        .hwndTarget = msg->hwnd,
+        .historyCount = 1,
+        .dwTime = msg->time,
+    };
+
+    info.pointerFlags = HIWORD( msg->wParam );
+    switch (msg->message)
+    {
+    case WM_POINTERUPDATE: info.pointerFlags |= POINTER_FLAG_UPDATE; break;
+    case WM_POINTERDOWN: info.pointerFlags |= POINTER_FLAG_DOWN; break;
+    case WM_POINTERUP: info.pointerFlags |= POINTER_FLAG_UP; break;
+    }
+    info.ptPixelLocation = info.ptPixelLocationRaw = location;
+
+    info.ptHimetricLocation.x = location.x * HIMETRIC_PER_INCH / system_dpi;
+    info.ptHimetricLocation.y = location.y * HIMETRIC_PER_INCH / system_dpi;
+    info.ptHimetricLocationRaw = info.ptHimetricLocation;
+
+    NtQueryPerformanceCounter( &counter, NULL );
+    info.PerformanceCount = counter.QuadPart;
+
+    return info;
+}
+
+static POINTER_BUTTON_CHANGE_TYPE compare_button( const POINTER_INFO *old, const POINTER_INFO *new )
+{
+    POINTER_BUTTON_CHANGE_TYPE change = POINTER_CHANGE_NONE;
+    static const struct
+    {
+        POINTER_FLAGS flag;
+        POINTER_BUTTON_CHANGE_TYPE down, up;
+    } map[] =
+    {
+        { POINTER_FLAG_FIRSTBUTTON, POINTER_CHANGE_FIRSTBUTTON_DOWN, POINTER_CHANGE_FIRSTBUTTON_UP },
+        { POINTER_FLAG_SECONDBUTTON, POINTER_CHANGE_SECONDBUTTON_DOWN, POINTER_CHANGE_SECONDBUTTON_UP },
+        { POINTER_FLAG_THIRDBUTTON, POINTER_CHANGE_THIRDBUTTON_DOWN, POINTER_CHANGE_THIRDBUTTON_UP },
+        { POINTER_FLAG_FOURTHBUTTON, POINTER_CHANGE_FOURTHBUTTON_DOWN, POINTER_CHANGE_FOURTHBUTTON_UP },
+        { POINTER_FLAG_FIFTHBUTTON, POINTER_CHANGE_FIFTHBUTTON_DOWN, POINTER_CHANGE_FIFTHBUTTON_UP },
+    };
+    POINTER_FLAGS down = ~old->pointerFlags & new->pointerFlags, up = old->pointerFlags & ~new->pointerFlags;
+
+    for (size_t i = 0; i < ARRAY_SIZE(map); i++)
+    {
+        if (down & map[i].flag) change |= map[i].down;
+        if (up & map[i].flag) change |= map[i].up;
+    }
+    return change;
+}
+
+void update_pointer_from_msg( POINTER_INPUT_TYPE type, const MSG *msg )
+{
+    POINTER_INFO info = pointer_info_from_msg( msg );
+    POINTER_BUTTON_CHANGE_TYPE buttons;
+    struct pointer *pointer;
+
+    TRACE( "updating pointer id %d.\n", info.pointerId );
+
+    if (!(pointer = find_pointer( info.pointerId )) && !(pointer = pointer_create( info.pointerId, type ))) return;
+
+    buttons = compare_button( &pointer->info, &info );
+    pointer->info = info;
+    pointer->info.pointerType = pointer->type;
+    pointer->info.ButtonChangeType = buttons;
+}
+
+static POINTER_INPUT_TYPE pointer_type_from_hw( const struct hw_msg_source *source )
+{
+    switch (source->origin)
+    {
+    case IMDT_PEN: return PT_PEN;
+    case IMDT_MOUSE: return PT_MOUSE;
+    case IMDT_TOUCH: return PT_TOUCH;
+    case IMDT_TOUCHPAD: return PT_TOUCHPAD;
+    default: return PT_POINTER;
+    }
+}
+
+/***********************************************************************
+ *          process_pointer_message
+ *
+ * returns TRUE if the contents of 'msg' should be passed to the application
+ */
+BOOL process_pointer_message( MSG *msg, UINT hw_id, const struct hardware_msg_data *msg_data )
+{
+    update_pointer_from_msg( pointer_type_from_hw( &msg_data->source ), msg );
+    msg->pt = point_phys_to_win_dpi( msg->hwnd, msg->pt );
+    return TRUE;
+}
+
 /**********************************************************************
  *       NtUserInitializeTouchInjection    (win32u.@)
  */
@@ -2884,9 +3096,69 @@ BOOL WINAPI NtUserInitializeTouchInjection( UINT max_count, UINT mode )
  */
 BOOL WINAPI NtUserGetPointerType( UINT32 id, POINTER_INPUT_TYPE *type )
 {
-    FIXME( "id %u, type %p stub!\n", id, type );
-    RtlSetLastWin32Error( ERROR_CALL_NOT_IMPLEMENTED );
-    return FALSE;
+    struct pointer *pointer;
+
+    TRACE( "%u, %p\n", id, type );
+
+    /* pointerid 1 is always the mouse */
+    if (id == 1 && type)
+    {
+        *type = PT_MOUSE;
+        return TRUE;
+    }
+
+    if (!id || !type || !(pointer = find_pointer( id )))
+    {
+        RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+
+    *type = pointer->type;
+    return TRUE;
+}
+
+/**********************************************************************
+ *       NtUserGetPointerInfoList    (win32u.@)
+ */
+BOOL WINAPI NtUserGetPointerInfoList( UINT32 id, POINTER_INPUT_TYPE type, UINT_PTR unk0, UINT_PTR unk1, SIZE_T size,
+                                      UINT32 *entry_count, UINT32 *pointer_count, void *pointer_info )
+{
+    struct pointer *pointer;
+    size_t target_size = 0;
+
+    TRACE( "id %d, type %#x, unk0 %#lx, unk1 %#lx, size %#lx, entry_count %p, pointer_count %p, pointer_info %p\n",
+            id, type, (long)unk0, (long)unk1, size, entry_count, pointer_count, pointer_info );
+
+    switch (type)
+    {
+    case PT_MOUSE:
+    case PT_PEN: target_size = sizeof(POINTER_PEN_INFO); break;
+    case PT_POINTER: target_size = sizeof(POINTER_INFO); break;
+    case PT_TOUCHPAD:
+    case PT_TOUCH: target_size = sizeof(POINTER_TOUCH_INFO); break;
+    }
+
+    if (type == PT_MOUSE || size != target_size)
+    {
+        RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+
+    if (!(pointer = find_pointer( id )))
+    {
+        TRACE( "pointer id %d not found.\n", id );
+        RtlSetLastWin32Error( ERROR_INVALID_PARAMETER );
+        return FALSE;
+    }
+
+    if (type != PT_POINTER) FIXME( "Pointer type %#x not implemented!", type );
+
+    *entry_count = 1;
+    *pointer_count = 1;
+
+    memset( pointer_info, 0, size );
+    *(POINTER_INFO *)pointer_info = pointer->info;
+    return TRUE;
 }
 
 /**********************************************************************
@@ -2903,9 +3175,9 @@ BOOL WINAPI NtUserGetPointerDeviceRects( HANDLE handle, RECT *device_rect, RECT
         return FALSE;
     }
 
-    rect = get_virtual_screen_rect( 0, MDT_DEFAULT );
-    SetRect( device_rect, 0, 0, (rect.right - rect.left) * HIMETRIC_PER_INCH / get_system_dpi(),
-             (rect.bottom - rect.top) * HIMETRIC_PER_INCH / get_system_dpi() );
+    rect = get_virtual_screen_rect( no_dpi, MDT_DEFAULT );
+    SetRect( device_rect, 0, 0, (rect.right - rect.left) * HIMETRIC_PER_INCH / system_dpi,
+             (rect.bottom - rect.top) * HIMETRIC_PER_INCH / system_dpi );
     *display_rect = get_virtual_screen_rect( get_thread_dpi(), MDT_DEFAULT );
 
     TRACE( "returning device %s, display %s\n", wine_dbgstr_rect(device_rect), wine_dbgstr_rect(display_rect) );
diff --git a/dlls/win32u/message.c b/dlls/win32u/message.c
index b6cdd3efa0ea..d432eb3c1af1 100644
--- a/dlls/win32u/message.c
+++ b/dlls/win32u/message.c
@@ -47,6 +47,8 @@ WINE_DECLARE_DEBUG_CHANNEL(relay);
 
 static const struct _KUSER_SHARED_DATA *user_shared_data = (struct _KUSER_SHARED_DATA *)0x7ffe0000;
 
+static const struct ratio no_dpi;
+
 static LONG atomic_load_long( const volatile LONG *ptr )
 {
 #if defined(__i386__) || defined(__x86_64__)
@@ -2474,17 +2476,6 @@ static void send_parent_notify( HWND hwnd, WORD event, WORD idChild, POINT pt )
     }
 }
 
-/***********************************************************************
- *          process_pointer_message
- *
- * returns TRUE if the contents of 'msg' should be passed to the application
- */
-static BOOL process_pointer_message( MSG *msg, UINT hw_id, const struct hardware_msg_data *msg_data )
-{
-    msg->pt = point_phys_to_win_dpi( msg->hwnd, msg->pt );
-    return TRUE;
-}
-
 /***********************************************************************
  *          process_keyboard_message
  *
@@ -2695,7 +2686,21 @@ static BOOL process_mouse_message( MSG *msg, UINT hw_id, ULONG_PTR extra_info, H
             break;
         }
 
-        if (message) send_message( msg->hwnd, message, MAKELONG( 1, flags ), MAKELONG( msg->pt.x, msg->pt.y ) );
+        if (message)
+        {
+            MSG pointer_msg =
+            {
+                .message = message,
+                .lParam = MAKELONG( msg->pt.x, msg->pt.y ),
+                .wParam = MAKELONG( 1, flags ),
+                .hwnd = msg->hwnd,
+                .time = msg->time,
+                .pt = msg->pt,
+            };
+
+            update_pointer_from_msg( PT_MOUSE, &pointer_msg );
+            send_message( msg->hwnd, message, pointer_msg.wParam, pointer_msg.lParam );
+        }
     }
 
     /* FIXME: is this really the right place for this hook? */
@@ -3116,7 +3121,7 @@ static int peek_message( MSG *msg, const struct peek_message_filter *filter )
                 RECT rect = {info.msg.pt.x, info.msg.pt.y, info.msg.pt.x, info.msg.pt.y};
                 MSLLHOOKSTRUCT hook;
 
-                rect = map_rect_raw_to_virt( rect, 0 );
+                rect = map_rect_raw_to_virt( rect, no_dpi );
                 info.msg.pt.x = rect.left;
                 info.msg.pt.y = rect.top;
 
@@ -3899,9 +3904,9 @@ LRESULT send_internal_message_timeout( DWORD dest_pid, DWORD dest_tid,
 }
 
 /***********************************************************************
- *		send_hardware_message
+ *		server_send_hardware_message
  */
-NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARAM lparam )
+NTSTATUS server_send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARAM lparam )
 {
     struct send_message_info info;
     int prev_x, prev_y, new_x, new_y;
@@ -3932,30 +3937,40 @@ NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARA
             req->input.mouse.flags = input->mi.dwFlags;
             req->input.mouse.time  = input->mi.time;
             req->input.mouse.info  = input->mi.dwExtraInfo;
+            if (lparam)
+            {
+                struct raw_mouse *raw = (struct raw_mouse *)lparam;
+                req->input.mouse.raw_count = raw->count;
+                wine_server_add_data( req, raw->data, raw->count * sizeof(*raw->data) );
+            }
             break;
         case INPUT_KEYBOARD:
-            if (input->ki.dwFlags & KEYEVENTF_SCANCODE)
+            req->input.kbd.vkey  = input->ki.wVk;
+            req->input.kbd.scan  = input->ki.wScan;
+            req->input.kbd.flags = input->ki.dwFlags;
+            req->input.kbd.time  = input->ki.time;
+            req->input.kbd.info  = input->ki.dwExtraInfo;
+
+            /* Handle the scancode resolution before sending data to wineserver, as it doesn't
+             * have access to keyboard layout tables and needs the vkey to start hook chain.
+             */
+            if (req->input.kbd.flags & KEYEVENTF_SCANCODE)
             {
-                UINT scan = input->ki.wScan;
+                UINT scan = input->ki.wScan, dummy;
                 /* TODO: Use the keyboard layout of the target hwnd, once
                  * NtUserGetKeyboardLayout supports non-current threads. */
                 HKL layout = NtUserGetKeyboardLayout( 0 );
-                if (flags & SEND_HWMSG_INJECTED)
-                {
-                    scan = scan & 0xff;
-                    if (input->ki.dwFlags & KEYEVENTF_EXTENDEDKEY) scan |= 0xe000;
-                }
-                req->input.kbd.vkey = map_scan_to_kbd_vkey( scan, layout );
-                req->input.kbd.scan = input->ki.wScan & 0xff;
-            }
-            else
-            {
-                req->input.kbd.vkey = input->ki.wVk;
-                req->input.kbd.scan = input->ki.wScan;
+
+                if (flags & SEND_HWMSG_INJECTED) scan = scan & 0xff;
+                if (req->input.kbd.flags & KEYEVENTF_EXTENDEDKEY) scan |= 0xe000;
+
+                req->input.kbd.vkey = map_scan_to_kbd_vkey( scan, layout, (flags & SEND_HWMSG_INJECTED) ? &dummy : &scan );
+                if (scan & ~0xff) req->input.kbd.flags |= KEYEVENTF_EXTENDEDKEY;
+                else req->input.kbd.flags &= ~KEYEVENTF_EXTENDEDKEY;
+
+                req->input.kbd.scan = scan & 0xff;
+                req->input.kbd.flags &= ~KEYEVENTF_SCANCODE;
             }
-            req->input.kbd.flags = input->ki.dwFlags & ~KEYEVENTF_SCANCODE;
-            req->input.kbd.time  = input->ki.time;
-            req->input.kbd.info  = input->ki.dwExtraInfo;
             break;
         case INPUT_HARDWARE:
             req->input.hw.msg    = input->hi.uMsg;
diff --git a/dlls/win32u/ntuser_private.h b/dlls/win32u/ntuser_private.h
index dc14811ce865..26bab84b2e7f 100644
--- a/dlls/win32u/ntuser_private.h
+++ b/dlls/win32u/ntuser_private.h
@@ -121,6 +121,9 @@ struct user_thread_info
     HIMC                          default_imc;            /* default input context */
     HKL                           kbd_layout;             /* Current keyboard layout */
     UINT                          kbd_layout_id;          /* Current keyboard layout ID */
+    struct raw_mouse              raw_mouse;              /* accumulated hardware raw mouse frames */
+    INPUT                         mouse_motion;           /* accumulated hardware mouse motion */
+    HWND                          mouse_hwnd;             /* hardware mouse motion target window */
     struct hardware_msg_data     *rawinput;               /* Current rawinput message data */
     UINT                          spy_indent;             /* Current spy indent */
     BOOL                          clipping_cursor;        /* thread is currently clipping */
@@ -128,6 +131,7 @@ struct user_thread_info
     struct session_thread_data   *session_data;           /* shared session thread data */
     struct mouse_tracking_info   *mouse_tracking_info;    /* NtUserTrackMouseEvent handling */
     struct opengl_thread_data    *opengl_data;            /* OpenGL private thread data */
+    struct list                   known_pointers;         /* list of known pointers */
 };
 
 extern struct user_thread_info *get_user_thread_info(void);
diff --git a/dlls/win32u/opengl.c b/dlls/win32u/opengl.c
index d666705b17b0..02f85c4fe346 100644
--- a/dlls/win32u/opengl.c
+++ b/dlls/win32u/opengl.c
@@ -193,6 +193,28 @@ static BOOL opengl_drawable_swap( struct opengl_drawable *drawable )
     return drawable->funcs->swap( drawable );
 }
 
+static void *internal_context_create( void *share, int *context_format )
+{
+    static const int attribs[] = { WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, 0 };
+    BOOL shared = TRUE;
+    void *context;
+    int format;
+
+    for (format = 1; format <= formats_count; format++)
+    {
+        struct wgl_pixel_format *desc = pixel_formats + format - 1;
+        if (!(desc->pfd.dwFlags & PFD_SUPPORT_OPENGL)) continue;
+        if (desc->pfd.iPixelType != PFD_TYPE_RGBA) continue;
+        if (desc->pfd.cColorBits < 24) continue;
+        if (!driver_funcs->p_context_create( format, share, attribs, &context, &shared )) continue;
+        if (context_format) *context_format = format;
+        return context;
+    }
+
+    WARN( "Failed to create internal %s context\n", global_context ? "global" : "thread" );
+    return NULL;
+}
+
 static BOOL make_null_context_current( struct opengl_drawable *drawable )
 {
     struct opengl_thread_data *data = get_opengl_thread_data();
@@ -200,17 +222,7 @@ static BOOL make_null_context_current( struct opengl_drawable *drawable )
 
     if (!data->null_context)
     {
-        for (format = 1; format <= formats_count; format++)
-        {
-            struct wgl_pixel_format *desc = pixel_formats + format - 1;
-            if (!(desc->pfd.dwFlags & PFD_SUPPORT_OPENGL)) continue;
-            if (desc->pfd.iPixelType != PFD_TYPE_RGBA) continue;
-            if (desc->pfd.cColorBits < 24) continue;
-            break;
-        }
-
-        if (format > formats_count) return FALSE;
-        driver_funcs->p_context_create( format, global_context, NULL, &data->null_context );
+        if (!(data->null_context = internal_context_create( global_context, &format ))) return FALSE;
         if (driver_funcs->p_null_surface_create) driver_funcs->p_null_surface_create( format, &data->null_surface );
     }
 
@@ -788,7 +800,7 @@ static UINT egldrv_pbuffer_bind( HDC hdc, struct opengl_drawable *drawable, GLen
     return -1; /* use default implementation */
 }
 
-static BOOL egldrv_context_create( int format, void *share, const int *attribs, void **context )
+static BOOL egldrv_context_create( int format, void *share, const int *attribs, void **context, BOOL *shared )
 {
     const struct opengl_funcs *funcs = &display_funcs;
     const struct egl_platform *egl = &display_egl;
@@ -1339,7 +1351,7 @@ static UINT nulldrv_pbuffer_bind( HDC hdc, struct opengl_drawable *drawable, GLe
     return -1; /* use default implementation */
 }
 
-static BOOL nulldrv_context_create( int format, void *share, const int *attribs, void **private )
+static BOOL nulldrv_context_create( int format, void *share, const int *attribs, void **private, BOOL *shared )
 {
     return FALSE;
 }
@@ -2274,6 +2286,7 @@ static int get_window_swap_interval( HWND hwnd )
 
 static BOOL win32u_context_create( struct opengl_context *context, HDC hdc, const int *attribs )
 {
+    BOOL shared = TRUE;
     int format;
 
     TRACE( "context %p, hdc %p, attribs %p\n", context, hdc, attribs );
@@ -2285,12 +2298,13 @@ static BOOL win32u_context_create( struct opengl_context *context, HDC hdc, cons
         else RtlSetLastWin32Error( ERROR_INVALID_HANDLE );
         return FALSE;
     }
-    if (!driver_funcs->p_context_create( format, global_context, attribs, &context->driver_private ))
+    if (!driver_funcs->p_context_create( format, global_context, attribs, &context->driver_private, &shared ))
     {
         WARN( "Failed to create driver context for context %p\n", context );
         return FALSE;
     }
     context->format = format;
+    if (!shared) opengl_client_context_from_client( context->client_context )->broken_sharing = TRUE;
 
     TRACE( "created context %p, format %u for driver context %p\n", context, format, context->driver_private );
     return TRUE;
@@ -2782,17 +2796,7 @@ static void display_funcs_init(void)
             init_device_info( egl, &display_funcs );
     }
 
-    if (!global_context)
-    {
-        for (int format = 1; format <= formats_count; format++)
-        {
-            struct wgl_pixel_format *desc = pixel_formats + format - 1;
-            if (!(desc->pfd.dwFlags & PFD_SUPPORT_OPENGL)) continue;
-            if (desc->pfd.iPixelType != PFD_TYPE_RGBA) continue;
-            if (desc->pfd.cColorBits < 24) continue;
-            if (driver_funcs->p_context_create( format, NULL, NULL, &global_context )) break;
-        }
-    }
+    if (!global_context) global_context = internal_context_create( NULL, NULL );
 }
 
 /***********************************************************************
diff --git a/dlls/win32u/scroll.c b/dlls/win32u/scroll.c
index 00450c20709e..e864b271225a 100644
--- a/dlls/win32u/scroll.c
+++ b/dlls/win32u/scroll.c
@@ -253,7 +253,7 @@ static BOOL get_scroll_bar_rect( HWND hwnd, int bar, RECT *rect, int *arrow_size
         if (info->page)
         {
             *thumb_size = muldiv( pixels,info->page, info->maxVal - info->minVal + 1 );
-            min_thumb_size = muldiv( SCROLL_MIN_THUMB, get_dpi_for_window( hwnd ), 96 );
+            min_thumb_size = map_user_dpi( SCROLL_MIN_THUMB, get_dpi_for_window( hwnd ) );
             if (*thumb_size < min_thumb_size) *thumb_size = min_thumb_size;
         }
         else *thumb_size = get_system_metrics( SM_CXVSCROLL );
@@ -475,7 +475,7 @@ static UINT get_thumb_val( HWND hwnd, int bar, RECT *rect, BOOL vertical, int po
         if (info->page)
         {
             thumb_size = muldiv( pixels, info->page, info->maxVal - info->minVal + 1 );
-            min_thumb_size = muldiv( SCROLL_MIN_THUMB, get_dpi_for_window( hwnd ), 96 );
+            min_thumb_size = map_user_dpi( SCROLL_MIN_THUMB, get_dpi_for_window( hwnd ) );
             if (thumb_size < min_thumb_size) thumb_size = min_thumb_size;
         }
         else thumb_size = get_system_metrics( SM_CXVSCROLL );
diff --git a/dlls/win32u/syscall.c b/dlls/win32u/syscall.c
index e9f36bdd32b2..ca1be7227ca1 100644
--- a/dlls/win32u/syscall.c
+++ b/dlls/win32u/syscall.c
@@ -197,6 +197,7 @@ struct user_thread_info *get_user_thread_info(void)
 
         info = calloc( 1, sizeof(*info) );
         pthread_setspecific( user_thread_info_key, info );
+        list_init( &info->known_pointers );
 
         if (teb)
         {
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c
index 239d13df6cc9..fdcdfc0a9462 100644
--- a/dlls/win32u/sysparams.c
+++ b/dlls/win32u/sysparams.c
@@ -292,7 +292,8 @@ union sysparam_all_entry
     struct sysparam_pref_entry   pref;
 };
 
-static UINT system_dpi;
+static const struct ratio no_dpi;
+UINT system_dpi;
 static RECT work_area;
 static DWORD process_layout = ~0u;
 
@@ -302,6 +303,34 @@ static pthread_mutex_t display_dc_lock = PTHREAD_MUTEX_INITIALIZER;
 static pthread_mutex_t user_mutex;
 static unsigned int user_lock_thread, user_lock_rec;
 
+static UINT gcd( UINT a, UINT b )
+{
+    UINT r;
+
+    for (;;)
+    {
+        if (!a) return b;
+        if (!b) return a;
+        r = a % b;
+        a = b;
+        b = r;
+    }
+}
+
+static struct ratio make_ratio( UINT num, UINT den )
+{
+    UINT d = gcd( num, den );
+    struct ratio r = { num / d, den / d };
+    assert( num / d < 65536 );
+    assert( den / d < 65536 );
+    return r;
+}
+
+static struct ratio min_ratio( struct ratio x, struct ratio y )
+{
+    return x.num * y.den <= y.num * x.den ? x : y;
+}
+
 void user_lock(void)
 {
     pthread_mutex_lock( &user_mutex );
@@ -2304,29 +2333,31 @@ static void monitor_virt_to_raw_ratio( struct monitor *monitor, UINT *num, UINT
 }
 
 /* display_lock must be held */
-static UINT monitor_get_dpi( struct monitor *monitor, MONITOR_DPI_TYPE type, UINT *dpi_x, UINT *dpi_y )
+static struct ratio monitor_get_dpi( struct monitor *monitor, MONITOR_DPI_TYPE type, struct ratio *dpi_x, struct ratio *dpi_y )
 {
+    struct ratio scale_x = {1, 1}, scale_y = {1, 1};
     struct source *source = monitor->source;
-    float scale_x = 1.0, scale_y = 1.0;
     UINT dpi;
 
     if (!source || !(dpi = source->dpi)) dpi = system_dpi;
     if (source && type != MDT_EFFECTIVE_DPI)
     {
-        scale_x = source->physical.dmPelsWidth / (float)source->current.dmPelsWidth;
-        scale_y = source->physical.dmPelsHeight / (float)source->current.dmPelsHeight;
+        scale_x.num = source->physical.dmPelsWidth;
+        scale_x.den = source->current.dmPelsWidth;
+        scale_y.num = source->physical.dmPelsHeight;
+        scale_y.den = source->current.dmPelsHeight;
     }
 
-    *dpi_x = round( dpi * scale_x );
-    *dpi_y = round( dpi * scale_y );
-    return min( *dpi_x, *dpi_y );
+    *dpi_x = make_ratio( dpi * scale_x.num, scale_x.den );
+    *dpi_y = make_ratio( dpi * scale_y.num, scale_y.den );
+    return min_ratio( *dpi_x, *dpi_y );
 }
 
 /* display_lock must be held */
-static RECT map_monitor_rect( struct monitor *monitor, RECT rect, UINT dpi_from, MONITOR_DPI_TYPE type_from,
-                              UINT dpi_to, MONITOR_DPI_TYPE type_to )
+static RECT map_monitor_rect( struct monitor *monitor, RECT rect, struct ratio dpi_from, MONITOR_DPI_TYPE type_from,
+                              struct ratio dpi_to, MONITOR_DPI_TYPE type_to )
 {
-    UINT x, y;
+    struct ratio x, y;
 
     assert( type_from != type_to );
 
@@ -2334,14 +2365,15 @@ static RECT map_monitor_rect( struct monitor *monitor, RECT rect, UINT dpi_from,
     {
         double points[4] = {rect.left, rect.top, rect.right, rect.bottom}, from[2], to[2];
         DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)}, physical_mode;
-        UINT num, den, dpi;
+        struct ratio dpi;
+        UINT num, den;
 
         source_get_current_settings( monitor->source, &current_mode );
         physical_mode = monitor->source->physical;
 
         dpi = monitor_get_dpi( monitor, MDT_DEFAULT, &x, &y );
-        if (!dpi_from) dpi_from = dpi;
-        if (!dpi_to) dpi_to = dpi;
+        if (!dpi_from.num) dpi_from = dpi;
+        if (!dpi_to.num) dpi_to = dpi;
 
         if (type_from == MDT_RAW_DPI)
         {
@@ -2362,11 +2394,11 @@ static RECT map_monitor_rect( struct monitor *monitor, RECT rect, UINT dpi_from,
 
         for (int i = 0; i < ARRAY_SIZE(points); i++)
         {
-            points[i] *= (double)dpi / dpi_from;
+            points[i] *= (double)dpi.num * dpi_from.den / (dpi_from.num * dpi.den);
             points[i] -= from[i & 1];
             points[i] *= (double)num / den;
             points[i] += to[i & 1];
-            points[i] *= (double)dpi_to / dpi;
+            points[i] *= (double)dpi_to.num * dpi.den / (dpi.num * dpi_to.den);
             points[i] = roundf( points[i] );
             points[i] = min( INT_MAX, max( INT_MIN, (INT64)points[i] ));
         }
@@ -2375,18 +2407,18 @@ static RECT map_monitor_rect( struct monitor *monitor, RECT rect, UINT dpi_from,
         return rect;
     }
 
-    if (!dpi_from) dpi_from = monitor_get_dpi( monitor, type_from, &x, &y );
-    if (!dpi_to) dpi_to = monitor_get_dpi( monitor, type_to, &x, &y );
+    if (!dpi_from.num) dpi_from = monitor_get_dpi( monitor, type_from, &x, &y );
+    if (!dpi_to.num) dpi_to = monitor_get_dpi( monitor, type_to, &x, &y );
     return map_dpi_rect( rect, dpi_from, dpi_to );
 }
 
 /* display_lock must be held */
-static RECT monitor_get_rect( struct monitor *monitor, UINT dpi, MONITOR_DPI_TYPE type )
+static RECT monitor_get_rect( struct monitor *monitor, struct ratio dpi, MONITOR_DPI_TYPE type )
 {
     DEVMODEW current_mode = {.dmSize = sizeof(DEVMODEW)};
     RECT rect = {0, 0, 1024, 768};
+    struct ratio dpi_from, x, y;
     struct source *source;
-    UINT dpi_from, x, y;
     DEVMODEW *mode;
 
     /* services do not have any adapters, only a virtual monitor */
@@ -2406,10 +2438,10 @@ static RECT monitor_get_rect( struct monitor *monitor, UINT dpi, MONITOR_DPI_TYP
 }
 
 /* display_lock must be held */
-static void monitor_get_info( struct monitor *monitor, MONITORINFO *info, UINT dpi )
+static void monitor_get_info( struct monitor *monitor, MONITORINFO *info, struct ratio dpi )
 {
     info->rcMonitor = monitor_get_rect( monitor, dpi, MDT_DEFAULT );
-    info->rcWork = map_monitor_rect( monitor, monitor->rc_work, 0, MDT_RAW_DPI, dpi, MDT_DEFAULT );
+    info->rcWork = map_monitor_rect( monitor, monitor->rc_work, no_dpi, MDT_RAW_DPI, dpi, MDT_DEFAULT );
     intersect_rect( &info->rcWork, &info->rcWork, &info->rcMonitor );
     info->dwFlags = is_monitor_primary( monitor ) ? MONITORINFOF_PRIMARY : 0;
 
@@ -2427,19 +2459,24 @@ static void set_winstation_monitors( BOOL increment )
 {
     struct monitor_info *infos, *info;
     struct monitor *monitor;
-    UINT count, x, y;
+    struct ratio x, y;
+    UINT count;
 
     if (!(count = list_count( &monitors ))) return;
     if (!(info = infos = calloc( count, sizeof(*infos) ))) return;
 
+    TRACE( "increment %u\n", increment );
     LIST_FOR_EACH_ENTRY( monitor, &monitors, struct monitor, entry )
     {
         if (is_monitor_primary( monitor )) info->flags |= MONITOR_FLAG_PRIMARY;
         if (!is_monitor_active( monitor )) info->flags |= MONITOR_FLAG_INACTIVE;
         if (monitor->is_clone) info->flags |= MONITOR_FLAG_CLONE;
         info->dpi = monitor_get_dpi( monitor, MDT_EFFECTIVE_DPI, &x, &y );
-        info->virt = wine_server_rectangle( monitor_get_rect( monitor, 0, MDT_EFFECTIVE_DPI ) );
-        info->raw = wine_server_rectangle( monitor_get_rect( monitor, 0, MDT_RAW_DPI ) );
+        info->raw_dpi = monitor_get_dpi( monitor, MDT_RAW_DPI, &x, &y );
+        info->virt = wine_server_rectangle( monitor_get_rect( monitor, no_dpi, MDT_EFFECTIVE_DPI ) );
+        info->raw = wine_server_rectangle( monitor_get_rect( monitor, no_dpi, MDT_RAW_DPI ) );
+        TRACE( "  flags %#x virt %s dpi %s raw %s raw_dpi %s\n", info->flags, wine_dbgstr_rect( (RECT *)&info->virt ),
+               debugstr_ratio( info->dpi ), wine_dbgstr_rect( (RECT *)&info->raw ), debugstr_ratio( info->raw_dpi ) );
         info++;
     }
 
@@ -2924,7 +2961,7 @@ HBITMAP get_display_bitmap(void)
     RECT virtual_rect;
     HBITMAP ret;
 
-    virtual_rect = get_virtual_screen_rect( 0, MDT_DEFAULT );
+    virtual_rect = get_virtual_screen_rect( no_dpi, MDT_DEFAULT );
     pthread_mutex_lock( &display_dc_lock );
     if (!EqualRect( &old_virtual_rect, &virtual_rect ))
     {
@@ -2944,7 +2981,7 @@ static void release_display_dc( HDC hdc )
 }
 
 /* display_lock must be held, keep in sync with server/window.c */
-static struct monitor *get_monitor_from_rect( RECT rect, UINT flags, UINT dpi, MONITOR_DPI_TYPE type )
+static struct monitor *get_monitor_from_rect( RECT rect, UINT flags, struct ratio dpi, MONITOR_DPI_TYPE type )
 {
     struct monitor *monitor, *primary = NULL, *nearest = NULL, *found = NULL;
     UINT max_area = 0, min_distance = -1;
@@ -3019,7 +3056,7 @@ static struct monitor *get_monitor_from_handle( HMONITOR handle )
 }
 
 /* display_lock must be held */
-static RECT monitors_get_union_rect( UINT dpi, MONITOR_DPI_TYPE type )
+static RECT monitors_get_union_rect( struct ratio dpi, MONITOR_DPI_TYPE type )
 {
     struct monitor *monitor;
     RECT rect = {0};
@@ -3036,35 +3073,35 @@ static RECT monitors_get_union_rect( UINT dpi, MONITOR_DPI_TYPE type )
 }
 
 /* map a monitor rect from MDT_RAW_DPI to MDT_DEFAULT coordinates */
-RECT map_rect_raw_to_virt( RECT rect, UINT dpi_to )
+RECT map_rect_raw_to_virt( RECT rect, struct ratio dpi_to )
 {
     RECT pos = {rect.left, rect.top, rect.left, rect.top};
     struct monitor *monitor;
 
     if (!lock_display_devices( FALSE )) return rect;
-    if ((monitor = get_monitor_from_rect( pos, MONITOR_DEFAULTTONEAREST, 0, MDT_RAW_DPI )))
-        rect = map_monitor_rect( monitor, rect, 0, MDT_RAW_DPI, dpi_to, MDT_DEFAULT );
+    if ((monitor = get_monitor_from_rect( pos, MONITOR_DEFAULTTONEAREST, no_dpi, MDT_RAW_DPI )))
+        rect = map_monitor_rect( monitor, rect, no_dpi, MDT_RAW_DPI, dpi_to, MDT_DEFAULT );
     unlock_display_devices();
 
     return rect;
 }
 
 /* map a monitor rect from MDT_DEFAULT to MDT_RAW_DPI coordinates */
-RECT map_rect_virt_to_raw( RECT rect, UINT dpi_from )
+RECT map_rect_virt_to_raw( RECT rect, struct ratio dpi_from )
 {
     RECT pos = {rect.left, rect.top, rect.left, rect.top};
     struct monitor *monitor;
 
     if (!lock_display_devices( FALSE )) return rect;
     if ((monitor = get_monitor_from_rect( pos, MONITOR_DEFAULTTONEAREST, dpi_from, MDT_DEFAULT )))
-        rect = map_monitor_rect( monitor, rect, dpi_from, MDT_DEFAULT, 0, MDT_RAW_DPI );
+        rect = map_monitor_rect( monitor, rect, dpi_from, MDT_DEFAULT, no_dpi, MDT_RAW_DPI );
     unlock_display_devices();
 
     return rect;
 }
 
 /* map (absolute) window rects from MDT_DEFAULT to MDT_RAW_DPI coordinates */
-struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UINT dpi_from )
+struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, struct ratio dpi_from )
 {
     RECT rect, monitor_rect, virt_visible_rect = rects.visible;
     struct monitor *monitor;
@@ -3073,9 +3110,9 @@ struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UIN
     if (!lock_display_devices( FALSE )) return rects;
     if ((monitor = get_monitor_from_rect( rects.window, MONITOR_DEFAULTTONEAREST, dpi_from, MDT_DEFAULT )))
     {
-        rects.visible = map_monitor_rect( monitor, rects.visible, dpi_from, MDT_DEFAULT, 0, MDT_RAW_DPI );
-        rects.window = map_monitor_rect( monitor, rects.window, dpi_from, MDT_DEFAULT, 0, MDT_RAW_DPI );
-        rects.client = map_monitor_rect( monitor, rects.client, dpi_from, MDT_DEFAULT, 0, MDT_RAW_DPI );
+        rects.visible = map_monitor_rect( monitor, rects.visible, dpi_from, MDT_DEFAULT, no_dpi, MDT_RAW_DPI );
+        rects.window = map_monitor_rect( monitor, rects.window, dpi_from, MDT_DEFAULT, no_dpi, MDT_RAW_DPI );
+        rects.client = map_monitor_rect( monitor, rects.client, dpi_from, MDT_DEFAULT, no_dpi, MDT_RAW_DPI );
     }
     /* if the visible rect is fullscreen, make it cover the full raw monitor, regardless of aspect ratio */
     LIST_FOR_EACH_ENTRY(monitor, &monitors, struct monitor, entry)
@@ -3086,7 +3123,7 @@ struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UIN
         is_fullscreen = intersect_rect( &rect, &monitor_rect, &virt_visible_rect ) && EqualRect( &rect, &monitor_rect );
         if (is_fullscreen)
         {
-            rect = monitor_get_rect( monitor, 0, MDT_RAW_DPI );
+            rect = monitor_get_rect( monitor, no_dpi, MDT_RAW_DPI );
             union_rect( &rects.visible, &rects.visible, &rect );
         }
     }
@@ -3095,54 +3132,18 @@ struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UIN
     return rects;
 }
 
-static UINT get_monitor_dpi( HMONITOR handle, UINT type, UINT *x, UINT *y )
+static struct ratio get_monitor_dpi( HMONITOR handle, UINT type, struct ratio *x, struct ratio *y )
 {
+    struct ratio dpi = {system_dpi, 1};
     struct monitor *monitor;
-    UINT dpi = system_dpi;
 
-    if (!lock_display_devices( FALSE )) return 0;
+    if (!lock_display_devices( FALSE )) return no_dpi;
     if ((monitor = get_monitor_from_handle( handle ))) dpi = monitor_get_dpi( monitor, type, x, y );
     unlock_display_devices();
 
     return dpi;
 }
 
-/**********************************************************************
- *              get_win_monitor_dpi
- */
-UINT get_win_monitor_dpi( HWND hwnd, UINT *raw_dpi )
-{
-    UINT dpi = NTUSER_DPI_CONTEXT_GET_DPI( get_window_dpi_awareness_context( hwnd ) );
-    HWND parent = get_parent( hwnd );
-    RECT rect = {0};
-    WND *win;
-
-    if (!(win = get_win_ptr( hwnd )))
-    {
-        RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
-        return 0;
-    }
-
-    if (win == WND_DESKTOP) return monitor_dpi_from_rect( rect, get_thread_dpi(), raw_dpi );
-    if (win == WND_OTHER_PROCESS)
-    {
-        if (!get_window_rect( hwnd, &rect, dpi )) return 0;
-    }
-    /* avoid recursive calls from get_window_rects for the process windows */
-    else if ((parent = win->parent) && parent != get_desktop_window())
-    {
-        release_win_ptr( win );
-        return get_win_monitor_dpi( parent, raw_dpi );
-    }
-    else
-    {
-        rect = is_iconic( hwnd ) ? win->normal_rect : win->rects.window;
-        release_win_ptr( win );
-    }
-
-    return monitor_dpi_from_rect( rect, dpi, raw_dpi );
-}
-
 /* keep in sync with user32 */
 static BOOL is_valid_dpi_awareness_context( UINT context, UINT dpi )
 {
@@ -3190,14 +3191,18 @@ DWORD get_process_layout(void)
 /**********************************************************************
  *              get_thread_dpi
  */
-UINT get_thread_dpi(void)
+struct ratio get_thread_dpi(void)
 {
+    struct ratio dpi = {1, 1};
+
     switch (NTUSER_DPI_CONTEXT_GET_AWARENESS( get_thread_dpi_awareness_context() ))
     {
-    case DPI_AWARENESS_UNAWARE:      return USER_DEFAULT_SCREEN_DPI;
-    case DPI_AWARENESS_SYSTEM_AWARE: return system_dpi;
-    default:                         return 0;  /* no scaling */
+    case DPI_AWARENESS_UNAWARE:      dpi.num = USER_DEFAULT_SCREEN_DPI; break;
+    case DPI_AWARENESS_SYSTEM_AWARE: dpi.num = system_dpi; break;
+    default: return no_dpi; /* no scaling */
     }
+
+    return dpi;
 }
 
 /* see GetDpiForSystem */
@@ -3229,17 +3234,23 @@ UINT set_thread_dpi_awareness_context( UINT context )
     return prev;
 }
 
+static BOOL needs_dpi_mapping( struct ratio dpi_from, struct ratio dpi_to )
+{
+    return dpi_from.num && dpi_to.num && memcmp( &dpi_from, &dpi_to, sizeof(struct ratio) );
+}
+
 /**********************************************************************
  *              map_dpi_rect
  */
-RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to )
+RECT map_dpi_rect( RECT rect, struct ratio dpi_from, struct ratio dpi_to )
 {
-    if (dpi_from && dpi_to && dpi_from != dpi_to)
+    if (needs_dpi_mapping( dpi_from, dpi_to ))
     {
-        rect.left   = muldiv( rect.left, dpi_to, dpi_from );
-        rect.top    = muldiv( rect.top, dpi_to, dpi_from );
-        rect.right  = muldiv( rect.right, dpi_to, dpi_from );
-        rect.bottom = muldiv( rect.bottom, dpi_to, dpi_from );
+        unsigned int num = dpi_to.num * dpi_from.den, den = dpi_from.num * dpi_to.den;
+        rect.left   = muldiv( rect.left, num, den );
+        rect.top    = muldiv( rect.top, num, den );
+        rect.right  = muldiv( rect.right, num, den );
+        rect.bottom = muldiv( rect.bottom, num, den );
     }
     return rect;
 }
@@ -3247,7 +3258,7 @@ RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to )
 /**********************************************************************
  *              map_dpi_region
  */
-HRGN map_dpi_region( HRGN hrgn, UINT dpi_from, UINT dpi_to )
+HRGN map_dpi_region( HRGN hrgn, struct ratio dpi_from, struct ratio dpi_to )
 {
     RGNDATA *data;
     UINT i, size;
@@ -3256,7 +3267,7 @@ HRGN map_dpi_region( HRGN hrgn, UINT dpi_from, UINT dpi_to )
     if (!(data = malloc( size ))) return 0;
     NtGdiGetRegionData( hrgn, size, data );
 
-    if (dpi_from && dpi_to && dpi_from != dpi_to)
+    if (needs_dpi_mapping( dpi_from, dpi_to ))
     {
         RECT *rects = (RECT *)data->Buffer;
         for (i = 0; i < data->rdh.nCount; i++) rects[i] = map_dpi_rect( rects[i], dpi_from, dpi_to );
@@ -3270,7 +3281,7 @@ HRGN map_dpi_region( HRGN hrgn, UINT dpi_from, UINT dpi_to )
 /**********************************************************************
  *              map_dpi_window_rects
  */
-struct window_rects map_dpi_window_rects( struct window_rects rects, UINT dpi_from, UINT dpi_to )
+struct window_rects map_dpi_window_rects( struct window_rects rects, struct ratio dpi_from, struct ratio dpi_to )
 {
     rects.window = map_dpi_rect( rects.window, dpi_from, dpi_to );
     rects.client = map_dpi_rect( rects.client, dpi_from, dpi_to );
@@ -3278,15 +3289,22 @@ struct window_rects map_dpi_window_rects( struct window_rects rects, UINT dpi_fr
     return rects;
 }
 
+/* map value from given DPI to user default screen DPI */
+UINT map_user_dpi( UINT value, struct ratio dpi_from )
+{
+    return muldiv( value, dpi_from.num, USER_DEFAULT_SCREEN_DPI );
+}
+
 /**********************************************************************
  *              map_dpi_point
  */
-POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to )
+POINT map_dpi_point( POINT pt, struct ratio dpi_from, struct ratio dpi_to )
 {
-    if (dpi_from && dpi_to && dpi_from != dpi_to)
+    if (needs_dpi_mapping( dpi_from, dpi_to ))
     {
-        pt.x = muldiv( pt.x, dpi_to, dpi_from );
-        pt.y = muldiv( pt.y, dpi_to, dpi_from );
+        unsigned int num = dpi_to.num * dpi_from.den, den = dpi_from.num * dpi_to.den;
+        pt.x = muldiv( pt.x, num, den );
+        pt.y = muldiv( pt.y, num, den );
     }
     return pt;
 }
@@ -3296,7 +3314,7 @@ POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to )
  */
 static POINT point_win_to_phys_dpi( HWND hwnd, POINT pt )
 {
-    UINT raw_dpi, dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
+    struct ratio raw_dpi, dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
     return map_dpi_point( pt, get_dpi_for_window( hwnd ), dpi );
 }
 
@@ -3305,7 +3323,7 @@ static POINT point_win_to_phys_dpi( HWND hwnd, POINT pt )
  */
 POINT point_phys_to_win_dpi( HWND hwnd, POINT pt )
 {
-    UINT raw_dpi, dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
+    struct ratio raw_dpi, dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
     return map_dpi_point( pt, dpi, get_dpi_for_window( hwnd ) );
 }
 
@@ -3314,8 +3332,8 @@ POINT point_phys_to_win_dpi( HWND hwnd, POINT pt )
  */
 POINT point_thread_to_win_dpi( HWND hwnd, POINT pt )
 {
-    UINT dpi = get_thread_dpi(), raw_dpi;
-    if (!dpi) dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
+    struct ratio dpi = get_thread_dpi(), raw_dpi;
+    if (!dpi.num) dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
     return map_dpi_point( pt, dpi, get_dpi_for_window( hwnd ));
 }
 
@@ -3324,8 +3342,8 @@ POINT point_thread_to_win_dpi( HWND hwnd, POINT pt )
  */
 RECT rect_thread_to_win_dpi( HWND hwnd, RECT rect )
 {
-    UINT dpi = get_thread_dpi(), raw_dpi;
-    if (!dpi) dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
+    struct ratio dpi = get_thread_dpi(), raw_dpi;
+    if (!dpi.num) dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
     return map_dpi_rect( rect, dpi, get_dpi_for_window( hwnd ) );
 }
 
@@ -3342,7 +3360,7 @@ static int map_to_dpi( int val, UINT dpi )
     return muldiv( val, dpi, USER_DEFAULT_SCREEN_DPI );
 }
 
-RECT get_virtual_screen_rect( UINT dpi, MONITOR_DPI_TYPE type )
+RECT get_virtual_screen_rect( struct ratio dpi, MONITOR_DPI_TYPE type )
 {
     RECT rect = {0};
 
@@ -3367,10 +3385,11 @@ static UINT get_display_index( const UNICODE_STRING *name )
 
 RECT get_display_rect( const WCHAR *display )
 {
+    struct ratio dpi = get_thread_dpi();
     struct monitor *monitor;
     UNICODE_STRING name;
     RECT rect = {0};
-    UINT index, dpi = get_thread_dpi();
+    UINT index;
 
     RtlInitUnicodeString( &name, display );
     if (!(index = get_display_index( &name ))) return rect;
@@ -3387,7 +3406,7 @@ RECT get_display_rect( const WCHAR *display )
     return rect;
 }
 
-RECT get_primary_monitor_rect( UINT dpi )
+RECT get_primary_monitor_rect( struct ratio dpi )
 {
     struct monitor *monitor;
     RECT rect = {0};
@@ -4667,7 +4686,7 @@ BOOL WINAPI NtUserEnumDisplayMonitors( HDC hdc, RECT *rect, MONITORENUMPROC proc
     return ret;
 }
 
-static BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info, UINT dpi )
+static BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info, struct ratio dpi )
 {
     struct monitor *monitor;
 
@@ -4691,23 +4710,24 @@ static BOOL get_monitor_info( HMONITOR handle, MONITORINFO *info, UINT dpi )
     return FALSE;
 }
 
-static HMONITOR monitor_from_rect( const RECT *rect, UINT flags, UINT dpi )
+static HMONITOR monitor_from_rect( const RECT *rect, UINT flags, struct ratio dpi_from )
 {
+    struct ratio dpi = {system_dpi, 1};
     struct monitor *monitor;
     HMONITOR ret = 0;
     RECT r;
 
-    r = map_dpi_rect( *rect, dpi, system_dpi );
+    r = map_dpi_rect( *rect, dpi_from, dpi );
 
     if (!lock_display_devices( FALSE )) return 0;
-    if ((monitor = get_monitor_from_rect( r, flags, system_dpi, MDT_DEFAULT ))) ret = monitor->handle;
+    if ((monitor = get_monitor_from_rect( r, flags, dpi, MDT_DEFAULT ))) ret = monitor->handle;
     unlock_display_devices();
 
     TRACE( "%s flags %x returning %p\n", wine_dbgstr_rect(rect), flags, ret );
     return ret;
 }
 
-MONITORINFO monitor_info_from_rect( RECT rect, UINT dpi )
+MONITORINFO monitor_info_from_rect( RECT rect, struct ratio dpi )
 {
     MONITORINFO info = {.cbSize = sizeof(info)};
     struct monitor *monitor;
@@ -4720,12 +4740,12 @@ MONITORINFO monitor_info_from_rect( RECT rect, UINT dpi )
     return info;
 }
 
-UINT monitor_dpi_from_rect( RECT rect, UINT dpi, UINT *raw_dpi )
+struct ratio monitor_dpi_from_rect( RECT rect, struct ratio dpi, struct ratio *raw_dpi )
 {
+    struct ratio ret = {system_dpi, 1}, x, y;
     struct monitor *monitor;
-    UINT ret = system_dpi, x, y;
 
-    if (!lock_display_devices( FALSE )) return 0;
+    if (!lock_display_devices( FALSE )) return no_dpi;
     if ((monitor = get_monitor_from_rect( rect, MONITOR_DEFAULTTONEAREST, dpi, MDT_DEFAULT )))
     {
         *raw_dpi = monitor_get_dpi( monitor, MDT_RAW_DPI, &x, &y );
@@ -4737,7 +4757,7 @@ UINT monitor_dpi_from_rect( RECT rect, UINT dpi, UINT *raw_dpi )
 }
 
 /* see MonitorFromWindow */
-HMONITOR monitor_from_window( HWND hwnd, UINT flags, UINT dpi )
+HMONITOR monitor_from_window( HWND hwnd, UINT flags, struct ratio dpi )
 {
     RECT rect;
     WINDOWPLACEMENT wp;
@@ -4745,7 +4765,7 @@ HMONITOR monitor_from_window( HWND hwnd, UINT flags, UINT dpi )
     TRACE( "(%p, 0x%08x)\n", hwnd, flags );
 
     wp.length = sizeof(wp);
-    if (is_iconic( hwnd ) && NtUserGetWindowPlacement( hwnd, &wp ))
+    if (is_iconic( hwnd ) && get_window_placement( hwnd, &wp ))
         return monitor_from_rect( &wp.rcNormalPosition, flags, dpi );
 
     if (get_window_rect( hwnd, &rect, dpi ))
@@ -4798,7 +4818,14 @@ BOOL WINAPI NtUserGetDpiForMonitor( HMONITOR monitor, UINT type, UINT *x, UINT *
     {
     case DPI_AWARENESS_UNAWARE:      *x = *y = USER_DEFAULT_SCREEN_DPI; break;
     case DPI_AWARENESS_SYSTEM_AWARE: *x = *y = system_dpi; break;
-    default:                         get_monitor_dpi( monitor, type, x, y ); break;
+    default:
+    {
+        struct ratio dpi_x, dpi_y;
+        get_monitor_dpi( monitor, type, &dpi_x, &dpi_y );
+        *x = round_dpi( dpi_x );
+        *y = round_dpi( dpi_y );
+        break;
+    }
     }
     return TRUE;
 }
@@ -4824,7 +4851,7 @@ BOOL WINAPI NtUserPerMonitorDPIPhysicalToLogicalPoint( HWND hwnd, POINT *pt )
     RECT rect;
     BOOL ret = FALSE;
 
-    if (get_window_rect( hwnd, &rect, 0 ) &&
+    if (get_window_rect( hwnd, &rect, no_dpi ) &&
         pt->x >= rect.left && pt->y >= rect.top && pt->x <= rect.right && pt->y <= rect.bottom)
     {
         *pt = point_phys_to_win_dpi( hwnd, *pt );
@@ -6347,7 +6374,7 @@ BOOL WINAPI NtUserSystemParametersInfo( UINT action, UINT val, void *ptr, UINT w
     case SPI_GETWORKAREA:
     {
         MONITORINFO info = {.cbSize = sizeof(info)};
-        UINT dpi = get_thread_dpi();
+        struct ratio dpi = get_thread_dpi();
 
         if (!ptr) return FALSE;
 
@@ -7462,6 +7489,7 @@ static void thread_detach(void)
     struct user_thread_info *thread_info = get_user_thread_info();
 
     destroy_thread_windows();
+    destroy_thread_pointers();
     user_driver->pThreadDetach();
 
     free( thread_info->rawinput );
@@ -7557,7 +7585,7 @@ ULONG_PTR WINAPI NtUserCallOneParam( ULONG_PTR arg, ULONG code )
         return get_sys_color( arg );
 
     case NtUserCallOneParam_GetPrimaryMonitorRect:
-        *(RECT *)arg = get_primary_monitor_rect( 0 );
+        *(RECT *)arg = get_primary_monitor_rect( no_dpi );
         return 1;
 
     case NtUserCallOneParam_GetSysColorBrush:
@@ -7626,7 +7654,7 @@ ULONG_PTR WINAPI NtUserCallTwoParam( ULONG_PTR arg1, ULONG_PTR arg2, ULONG code
     }
 
     case NtUserCallTwoParam_GetVirtualScreenRect:
-        *(RECT *)arg1 = get_virtual_screen_rect( 0, arg2 );
+        *(RECT *)arg1 = get_virtual_screen_rect( no_dpi, arg2 );
         return 1;
 
     /* temporary exports */
diff --git a/dlls/win32u/tests/win32u.c b/dlls/win32u/tests/win32u.c
index a67b918626d1..f13c4f34870c 100644
--- a/dlls/win32u/tests/win32u.c
+++ b/dlls/win32u/tests/win32u.c
@@ -1941,7 +1941,6 @@ static void test_NtUserGetPointerInfoList( BOOL mouse_in_pointer_enabled )
     entry_count = pointer_count = 2;
     ret = NtUserGetPointerInfoList( 1, PT_POINTER, 0, 0, sizeof(POINTER_INFO), &entry_count, &pointer_count, pointer_info );
     ok( !ret, "NtUserGetPointerInfoList succeeded\n" );
-    todo_wine
     ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() );
     ok( pointer_count == 2, "got pointer_count %u\n", pointer_count );
     ok( entry_count == 2, "got entry_count %u\n", entry_count );
@@ -1956,7 +1955,6 @@ static void test_NtUserGetPointerInfoList( BOOL mouse_in_pointer_enabled )
     entry_count = pointer_count = 2;
     ret = NtUserGetPointerInfoList( 1, PT_POINTER, 0, 0, sizeof(POINTER_INFO), &entry_count, &pointer_count, pointer_info );
     ok( !ret, "NtUserGetPointerInfoList succeeded\n" );
-    todo_wine
     ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() );
     ok( pointer_count == 2, "got pointer_count %u\n", pointer_count );
     ok( entry_count == 2, "got entry_count %u\n", entry_count );
@@ -1973,11 +1971,9 @@ static void test_NtUserGetPointerInfoList( BOOL mouse_in_pointer_enabled )
     memset( pointer_info, 0xcd, sizeof(pointer_info) );
     entry_count = pointer_count = 2;
     ret = NtUserGetPointerInfoList( 1, PT_POINTER, 0, 0, sizeof(POINTER_INFO), &entry_count, &pointer_count, pointer_info );
-    todo_wine_if(mouse_in_pointer_enabled)
     ok( ret == mouse_in_pointer_enabled, "NtUserGetPointerInfoList failed, error %lu\n", GetLastError() );
     if (!ret)
     {
-        todo_wine
         ok( GetLastError() == ERROR_INVALID_PARAMETER, "got error %lu\n", GetLastError() );
         goto done;
     }
diff --git a/dlls/win32u/vulkan.c b/dlls/win32u/vulkan.c
index 310ce8923c95..1bb4735587fb 100644
--- a/dlls/win32u/vulkan.c
+++ b/dlls/win32u/vulkan.c
@@ -1574,9 +1574,9 @@ static void win32u_vkDestroySurfaceKHR( VkInstance client_instance, VkSurfaceKHR
     free( surface );
 }
 
-static BOOL get_surface_rect( HWND hwnd, RECT *rect, UINT dpi )
+static BOOL get_surface_rect( HWND hwnd, RECT *rect, struct ratio dpi )
 {
-    if (!NtUserGetPresentRect( hwnd, rect, dpi ) && !NtUserGetClientRect( hwnd, rect, dpi )) return FALSE;
+    if (!get_present_rect( hwnd, rect, dpi ) && !get_client_rect( hwnd, rect, dpi )) return FALSE;
     OffsetRect( rect, -rect->left, -rect->top );
     return TRUE;
 }
@@ -1597,7 +1597,7 @@ static void adjust_surface_capabilities( struct vulkan_instance *instance, struc
 
     /* Update the image extents to match what the Win32 WSI would provide. */
     /* FIXME: handle DPI scaling, somehow */
-    get_surface_rect( surface->hwnd, &client_rect, NtUserGetDpiForWindow( surface->hwnd ) );
+    get_surface_rect( surface->hwnd, &client_rect, get_dpi_for_window( surface->hwnd ) );
     capabilities->minImageExtent.width = client_rect.right - client_rect.left;
     capabilities->minImageExtent.height = client_rect.bottom - client_rect.top;
     capabilities->maxImageExtent.width = client_rect.right - client_rect.left;
@@ -1825,6 +1825,7 @@ static VkResult win32u_vkCreateSwapchainKHR( VkDevice client_device, const VkSwa
     VkSwapchainCreateInfoKHR create_info_host = *create_info;
     VkSurfaceCapabilitiesKHR capabilities;
     VkSwapchainKHR host_swapchain;
+    struct ratio raw_dpi;
     RECT client_rect;
     VkResult res;
 
@@ -1848,7 +1849,8 @@ static VkResult win32u_vkCreateSwapchainKHR( VkDevice client_device, const VkSwa
      * display mode change emulation), MoltenVK's vkQueuePresentKHR returns VK_SUBOPTIMAL_KHR.
      * Create the swapchain with VkSwapchainPresentScalingCreateInfoEXT to avoid this.
      */
-    if (get_surface_rect( surface->hwnd, &client_rect, NtUserGetWinMonitorDpi( surface->hwnd, MDT_RAW_DPI ) ) &&
+    get_win_monitor_dpi( surface->hwnd, &raw_dpi );
+    if (get_surface_rect( surface->hwnd, &client_rect, raw_dpi ) &&
         !extents_equals( &create_info_host.imageExtent, &client_rect ) &&
         instance->extensions.has_VK_EXT_surface_maintenance1 &&
         physical_device->extensions.has_VK_KHR_swapchain_maintenance1)
@@ -1907,7 +1909,7 @@ static VkResult win32u_vkAcquireNextImage2KHR( VkDevice client_device, const VkA
     acquire_info_host.fence = fence ? fence->host.fence : 0;
     res = device->p_vkAcquireNextImage2KHR( device->host.device, &acquire_info_host, image_index );
 
-    if (!res && get_surface_rect( surface->hwnd, &client_rect, NtUserGetDpiForWindow( surface->hwnd ) ) &&
+    if (!res && get_surface_rect( surface->hwnd, &client_rect, get_dpi_for_window( surface->hwnd ) ) &&
         !extents_equals( &swapchain->extents, &client_rect ))
     {
         WARN( "Swapchain size %dx%d does not match client rect %s, returning VK_SUBOPTIMAL_KHR\n",
@@ -1933,7 +1935,7 @@ static VkResult win32u_vkAcquireNextImageKHR( VkDevice client_device, VkSwapchai
                                               semaphore ? semaphore->host.semaphore : 0, fence ? fence->host.fence : 0,
                                               image_index );
 
-    if (!res && get_surface_rect( surface->hwnd, &client_rect, NtUserGetDpiForWindow( surface->hwnd ) ) &&
+    if (!res && get_surface_rect( surface->hwnd, &client_rect, get_dpi_for_window( surface->hwnd ) ) &&
         !extents_equals( &swapchain->extents, &client_rect ))
     {
         WARN( "Swapchain size %dx%d does not match client rect %s, returning VK_SUBOPTIMAL_KHR\n",
@@ -1994,7 +1996,7 @@ static VkResult win32u_vkQueuePresentKHR( VkQueue client_queue, const VkPresentI
         client_surface_present( surface->client );
 
         if (swapchain_res < VK_SUCCESS) continue;
-        if (!get_surface_rect( surface->hwnd, &client_rect, NtUserGetDpiForWindow( surface->hwnd ) ))
+        if (!get_surface_rect( surface->hwnd, &client_rect, get_dpi_for_window( surface->hwnd ) ))
         {
             WARN( "Swapchain window %p is invalid, returning VK_ERROR_OUT_OF_DATE_KHR\n", surface->hwnd );
             if (present_info->pResults) present_info->pResults[i] = VK_ERROR_OUT_OF_DATE_KHR;
diff --git a/dlls/win32u/win32u_private.h b/dlls/win32u/win32u_private.h
index c217aa9ead6c..4e2b9283ba15 100644
--- a/dlls/win32u/win32u_private.h
+++ b/dlls/win32u/win32u_private.h
@@ -45,9 +45,9 @@ extern HICON create_small_icon( HICON handle );
 
 /* dce.c */
 extern struct window_surface dummy_surface;
-extern void create_window_surface( HWND hwnd, BOOL create_layered, const RECT *surface_rect, UINT monitor_dpi,
+extern void create_window_surface( HWND hwnd, BOOL create_layered, const RECT *surface_rect, struct ratio monitor_dpi,
                                    struct window_surface **window_surface );
-extern struct window_surface *get_driver_window_surface( struct window_surface *surface, UINT monitor_dpi );
+extern struct window_surface *get_driver_window_surface( struct window_surface *surface, struct ratio monitor_dpi );
 extern void erase_now( HWND hwnd, UINT rdw_flags );
 extern void flush_window_surfaces( BOOL idle );
 extern void move_window_bits( HWND hwnd, const struct window_rects *rects, const RECT *valid_rects );
@@ -110,7 +110,11 @@ extern void update_mouse_tracking_info( HWND hwnd );
 extern void update_current_mouse_window( HWND hwnd, INT hittest, POINT pos );
 extern BOOL process_wine_clipcursor( HWND hwnd, UINT flags, BOOL reset );
 extern BOOL clip_fullscreen_window( HWND hwnd, BOOL reset );
-extern USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout );
+extern USHORT map_scan_to_kbd_vkey( USHORT scan, HKL layout, UINT *mapped );
+extern void destroy_thread_pointers(void);
+extern BOOL process_pointer_message( MSG *msg, UINT hw_id, const struct hardware_msg_data *msg_data );
+extern void update_pointer_from_msg( POINTER_INPUT_TYPE type, const MSG *msg );
+extern NTSTATUS send_hardware_input( HWND hwnd, UINT flags, const INPUT *input, LPARAM lparam );
 
 /* menu.c */
 extern UINT draw_nc_menu_bar( HDC hdc, RECT *rect, HWND hwnd );
@@ -129,7 +133,7 @@ extern void track_keyboard_menu_bar( HWND hwnd, UINT wparam, WCHAR ch );
 extern void track_mouse_menu_bar( HWND hwnd, INT ht, int x, int y );
 
 /* message.c */
-extern NTSTATUS send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARAM lparam );
+extern NTSTATUS server_send_hardware_message( HWND hwnd, UINT flags, const INPUT *input, LPARAM lparam );
 extern LRESULT send_internal_message_timeout( DWORD dest_pid, DWORD dest_tid, UINT msg, WPARAM wparam,
                                               LPARAM lparam, UINT flags, UINT timeout,
                                               PDWORD_PTR res_ptr );
@@ -155,6 +159,7 @@ extern void set_standard_scroll_painted( HWND hwnd, int bar, BOOL painted );
 extern void track_scroll_bar( HWND hwnd, int scrollbar, POINT pt );
 
 /* sysparams.c */
+extern UINT system_dpi;
 extern BOOL decorated_mode;
 extern UINT64 thunk_lock_callback;
 extern HBRUSH get_55aa_brush(void);
@@ -163,34 +168,35 @@ extern LONG get_char_dimensions( HDC hdc, TEXTMETRICW *metric, int *height );
 extern HBITMAP get_display_bitmap(void);
 extern INT get_display_depth( UNICODE_STRING *name );
 extern RECT get_display_rect( const WCHAR *display );
-extern UINT get_win_monitor_dpi( HWND hwnd, UINT *raw_dpi );
-extern RECT get_primary_monitor_rect( UINT dpi );
+extern struct ratio get_win_monitor_dpi( HWND hwnd, struct ratio *raw_dpi );
+extern RECT get_primary_monitor_rect( struct ratio dpi );
 extern DWORD get_process_layout(void);
 extern COLORREF get_sys_color( int index );
 extern HBRUSH get_sys_color_brush( unsigned int index );
 extern HPEN get_sys_color_pen( unsigned int index );
 extern UINT get_system_dpi(void);
 extern int get_system_metrics( int index );
-extern UINT get_thread_dpi(void);
+extern struct ratio get_thread_dpi(void);
 extern UINT set_thread_dpi_awareness_context( UINT context );
 extern UINT get_thread_dpi_awareness_context(void);
-extern RECT get_virtual_screen_rect( UINT dpi, MONITOR_DPI_TYPE type );
+extern RECT get_virtual_screen_rect( struct ratio dpi, MONITOR_DPI_TYPE type );
 extern const char *gpu_device_name( UINT16 vendor, UINT16 device, const char *name );
 extern BOOL is_exiting_thread( DWORD tid );
-extern POINT map_dpi_point( POINT pt, UINT dpi_from, UINT dpi_to );
-extern RECT map_dpi_rect( RECT rect, UINT dpi_from, UINT dpi_to );
-extern HRGN map_dpi_region( HRGN region, UINT dpi_from, UINT dpi_to );
-extern struct window_rects map_dpi_window_rects( struct window_rects rects, UINT dpi_from, UINT dpi_to );
-extern RECT map_rect_raw_to_virt( RECT rect, UINT dpi_to );
-extern RECT map_rect_virt_to_raw( RECT rect, UINT dpi_from );
-extern struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, UINT dpi_from );
+extern UINT map_user_dpi( UINT value, struct ratio dpi_from );
+extern POINT map_dpi_point( POINT pt, struct ratio dpi_from, struct ratio dpi_to );
+extern RECT map_dpi_rect( RECT rect, struct ratio dpi_from, struct ratio dpi_to );
+extern HRGN map_dpi_region( HRGN region, struct ratio dpi_from, struct ratio dpi_to );
+extern struct window_rects map_dpi_window_rects( struct window_rects rects, struct ratio dpi_from, struct ratio dpi_to );
+extern RECT map_rect_raw_to_virt( RECT rect, struct ratio dpi_to );
+extern RECT map_rect_virt_to_raw( RECT rect, struct ratio dpi_from );
+extern struct window_rects map_window_rects_virt_to_raw( struct window_rects rects, struct ratio dpi_from );
 extern POINT point_phys_to_win_dpi( HWND hwnd, POINT pt );
 extern POINT point_thread_to_win_dpi( HWND hwnd, POINT pt );
 extern RECT rect_thread_to_win_dpi( HWND hwnd, RECT rect );
-extern HMONITOR monitor_from_window( HWND hwnd, UINT flags, UINT dpi );
-extern MONITORINFO monitor_info_from_rect( RECT rect, UINT dpi );
+extern HMONITOR monitor_from_window( HWND hwnd, UINT flags, struct ratio dpi );
+extern MONITORINFO monitor_info_from_rect( RECT rect, struct ratio dpi );
 extern MONITORINFO monitor_info_from_window( HWND hwnd, UINT flags );
-extern UINT monitor_dpi_from_rect( RECT rect, UINT dpi, UINT *raw_dpi );
+extern struct ratio monitor_dpi_from_rect( RECT rect, struct ratio dpi, struct ratio *raw_dpi );
 extern BOOL update_display_cache( BOOL force );
 extern void reset_monitor_update_serial(void);
 extern void user_lock(void);
@@ -270,10 +276,10 @@ struct tagWND;
 extern BOOL client_to_screen( HWND hwnd, POINT *pt );
 extern void destroy_thread_windows(void);
 extern LRESULT destroy_window( HWND hwnd );
-extern BOOL get_client_rect( HWND hwnd, RECT *rect, UINT dpi );
-extern BOOL get_present_rect( HWND hwnd, RECT *rect, UINT dpi );
+extern BOOL get_client_rect( HWND hwnd, RECT *rect, struct ratio dpi );
+extern BOOL get_present_rect( HWND hwnd, RECT *rect, struct ratio dpi );
 extern HWND get_desktop_window(void);
-extern UINT get_dpi_for_window( HWND hwnd );
+extern struct ratio get_dpi_for_window( HWND hwnd );
 extern HWND get_full_window_handle( HWND hwnd );
 extern HWND get_parent( HWND hwnd );
 extern HWND get_hwnd_message_parent(void);
@@ -295,15 +301,16 @@ extern int get_window_pixel_format( HWND hwnd );
 extern DWORD get_window_long( HWND hwnd, INT offset );
 extern UINT get_window_fnid( HWND hwnd );
 extern ULONG_PTR get_window_long_ptr( HWND hwnd, INT offset, BOOL ansi );
-extern BOOL get_window_rect( HWND hwnd, RECT *rect, UINT dpi );
+extern BOOL get_window_placement( HWND hwnd, WINDOWPLACEMENT *placement );
+extern BOOL get_window_rect( HWND hwnd, RECT *rect, struct ratio dpi );
 enum coords_relative;
-extern BOOL get_window_rect_rel( HWND hwnd, enum coords_relative rel, RECT *rect, UINT dpi );
-extern BOOL get_client_rect_rel( HWND hwnd, enum coords_relative rel, RECT *rect, UINT dpi );
+extern BOOL get_window_rect_rel( HWND hwnd, enum coords_relative rel, RECT *rect, struct ratio dpi );
+extern BOOL get_client_rect_rel( HWND hwnd, enum coords_relative rel, RECT *rect, struct ratio dpi );
 extern BOOL get_window_rects( HWND hwnd, enum coords_relative relative,
-                              struct window_rects *rects, UINT dpi );
+                              struct window_rects *rects, struct ratio dpi );
 extern HWND *list_window_children( HWND hwnd );
 extern int map_window_points( HWND hwnd_from, HWND hwnd_to, POINT *points, UINT count,
-                              UINT dpi );
+                              struct ratio dpi );
 extern void map_window_region( HWND from, HWND to, HRGN hrgn );
 extern BOOL screen_to_client( HWND hwnd, POINT *pt );
 extern LONG_PTR set_window_long( HWND hwnd, INT offset, UINT size, LONG_PTR newval,
@@ -448,4 +455,15 @@ static inline BOOL intersect_rect( RECT *dst, const RECT *src1, const RECT *src2
     return !IsRectEmpty( dst );
 }
 
+static inline UINT round_dpi( struct ratio dpi )
+{
+    if (!dpi.den) return 0;
+    return (dpi.num + dpi.den / 2) / dpi.den;
+}
+
+static inline const char *debugstr_ratio( struct ratio q )
+{
+    return wine_dbg_sprintf( "%d:%d", q.num, q.den );
+}
+
 #endif /* __WINE_WIN32U_PRIVATE */
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c
index f4dc335c5cf3..924990e8d4ff 100644
--- a/dlls/win32u/window.c
+++ b/dlls/win32u/window.c
@@ -37,6 +37,8 @@ WINE_DEFAULT_DEBUG_CHANNEL(win);
 #define USER_HANDLE_TO_INDEX(hwnd) ((LOWORD(hwnd) - FIRST_USER_HANDLE) >> 1)
 #define USER_HANDLE_FROM_INDEX(index, generation) UlongToHandle( (index << 1) + FIRST_USER_HANDLE + (generation << 16) )
 
+static const struct ratio no_dpi;
+
 static void *client_objects[MAX_USER_HANDLES];
 
 #define SWP_AGG_NOGEOMETRYCHANGE \
@@ -308,6 +310,7 @@ void detach_client_surfaces( HWND hwnd )
         client_surface_add_ref( surface );
 
         surface->funcs->detach( surface );
+        surface->toplevel = NULL;
         surface->hwnd = NULL;
     }
 
@@ -320,7 +323,39 @@ void detach_client_surfaces( HWND hwnd )
     }
 }
 
-static void update_client_surfaces( HWND hwnd )
+static RECT get_client_surface_rects( HWND toplevel, HWND hwnd, RECT *monitor_rect )
+{
+    struct ratio dpi = get_dpi_for_window( hwnd ), raw_dpi;
+    struct window_rects rects, monitor_rects;
+    RECT rect = {0};
+
+    if (!toplevel) toplevel = NtUserGetAncestor( hwnd, GA_ROOT );
+    get_window_rects( toplevel, COORDS_PARENT, &rects, dpi );
+    monitor_rects = map_window_rects_virt_to_raw( rects, dpi );
+
+    if (get_present_rect( hwnd, &rect, dpi )) OffsetRect( &rect, -rects.client.left, -rects.client.top );
+    else if (get_client_rect( hwnd, &rect, dpi )) map_window_points( hwnd, toplevel, (POINT *)&rect, 2, dpi );
+
+    get_win_monitor_dpi( hwnd, &raw_dpi );
+    *monitor_rect = map_dpi_rect( rect, dpi, raw_dpi );
+    OffsetRect( monitor_rect, monitor_rects.client.left - monitor_rects.visible.left,
+                monitor_rects.client.top - monitor_rects.visible.top );
+
+    return rect;
+}
+
+static void client_surface_update_locked( struct client_surface *surface )
+{
+    surface->toplevel = NtUserGetAncestor( surface->hwnd, GA_ROOT );
+    surface->virtual_rect = get_client_surface_rects( surface->toplevel, surface->hwnd, &surface->monitor_rect );
+
+    TRACE( "updating %s, toplevel %p, virtual_rect %s, monitor_rect %s\n", debugstr_client_surface( surface ), surface->toplevel,
+           wine_dbgstr_rect( &surface->virtual_rect ), wine_dbgstr_rect( &surface->monitor_rect ) );
+    surface->funcs->update( surface );
+    InterlockedExchange( &surface->updated, 1 );
+}
+
+void update_client_surfaces( HWND hwnd )
 {
     struct client_surface *surface, *next;
 
@@ -329,8 +364,7 @@ static void update_client_surfaces( HWND hwnd )
     LIST_FOR_EACH_ENTRY_SAFE( surface, next, &client_surfaces, struct client_surface, entry )
     {
         if (NtUserGetAncestor( surface->hwnd, GA_ROOT ) != hwnd) continue;
-        surface->funcs->update( surface );
-        InterlockedExchange( &surface->updated, 1 );
+        client_surface_update_locked( surface );
     }
 
     pthread_mutex_unlock( &surfaces_lock );
@@ -338,15 +372,19 @@ static void update_client_surfaces( HWND hwnd )
 
 void *client_surface_create( UINT size, const struct client_surface_funcs *funcs, HWND hwnd )
 {
+    HWND toplevel = NtUserGetAncestor( hwnd, GA_ROOT );
     struct client_surface *surface;
 
     if (!(surface = calloc( 1, size ))) return NULL;
     surface->funcs = funcs;
     surface->ref = 1;
     surface->hwnd = hwnd;
+    surface->toplevel = toplevel;
+    surface->virtual_rect = get_client_surface_rects( toplevel, hwnd, &surface->monitor_rect );
     list_init( &surface->entry );
 
-    TRACE( "created %s\n", debugstr_client_surface( surface ) );
+    TRACE( "created %s, toplevel %p, virtual_rect %s, monitor_rect %s\n", debugstr_client_surface( surface ), toplevel,
+           wine_dbgstr_rect( &surface->virtual_rect ), wine_dbgstr_rect( &surface->monitor_rect ) );
     return surface;
 }
 
@@ -384,6 +422,7 @@ void client_surface_present( struct client_surface *surface )
     pthread_mutex_lock( &surfaces_lock );
     if ((hwnd = surface->hwnd))
     {
+        client_surface_update_locked( surface );
         if (surface->offscreen) hdc = NtUserGetDCEx( hwnd, 0, DCX_CACHE | DCX_USESTYLE );
         surface->funcs->present( surface, hdc );
         if (hdc) NtUserReleaseDC( hwnd, hdc );
@@ -394,7 +433,7 @@ void client_surface_present( struct client_surface *surface )
 void client_surface_update( struct client_surface *surface )
 {
     pthread_mutex_lock( &surfaces_lock );
-    if (surface->hwnd) surface->funcs->update( surface );
+    if (surface->hwnd) client_surface_update_locked( surface );
     pthread_mutex_unlock( &surfaces_lock );
 }
 
@@ -404,7 +443,7 @@ void add_window_client_surface( HWND hwnd, struct client_surface *surface )
 
     surface->hwnd = hwnd;
     list_add_tail( &client_surfaces, &surface->entry );
-    surface->funcs->update( surface );
+    client_surface_update_locked( surface );
 
     pthread_mutex_unlock( &surfaces_lock );
 }
@@ -647,7 +686,7 @@ HWND WINAPI NtUserSetParent( HWND hwnd, HWND parent )
     if (parent && parent != NtUserGetDesktopWindow()) win->has_icons = FALSE;
 
     get_window_rect_rel( hwnd, COORDS_PARENT, &window_rect, get_dpi_for_window(hwnd) );
-    get_window_rect_rel( hwnd, COORDS_SCREEN, &old_screen_rect, 0 );
+    get_window_rect_rel( hwnd, COORDS_SCREEN, &old_screen_rect, no_dpi );
 
     SERVER_START_REQ( set_parent )
     {
@@ -664,7 +703,7 @@ HWND WINAPI NtUserSetParent( HWND hwnd, HWND parent )
     release_win_ptr( win );
     if (!ret) return 0;
 
-    get_window_rect_rel( hwnd, COORDS_SCREEN, &new_screen_rect, 0 );
+    get_window_rect_rel( hwnd, COORDS_SCREEN, &new_screen_rect, no_dpi );
     context = set_thread_dpi_awareness_context( get_window_dpi_awareness_context( hwnd ));
 
     user_driver->pSetParent( full_handle, parent, old_parent );
@@ -1073,6 +1112,27 @@ BOOL is_window_enabled( HWND hwnd )
     return !(ret & WS_DISABLED);
 }
 
+struct ratio get_win_monitor_dpi( HWND hwnd, struct ratio *raw_dpi )
+{
+    struct object_lock lock = OBJECT_LOCK_INIT;
+    const window_shm_t *window_shm;
+    struct ratio dpi = no_dpi;
+    NTSTATUS status;
+
+    while ((status = get_shared_window( hwnd, &lock, &window_shm )) == STATUS_PENDING)
+    {
+        *raw_dpi = window_shm->raw_dpi;
+        dpi = window_shm->dpi;
+    }
+    if (status)
+    {
+        RtlSetLastWin32Error( ERROR_INVALID_WINDOW_HANDLE );
+        return no_dpi;
+    }
+
+    return dpi;
+}
+
 /* see GetWindowDpiAwarenessContext */
 UINT get_window_dpi_awareness_context( HWND hwnd )
 {
@@ -1092,11 +1152,13 @@ UINT get_window_dpi_awareness_context( HWND hwnd )
 }
 
 /* see GetDpiForWindow */
-UINT get_dpi_for_window( HWND hwnd )
+struct ratio get_dpi_for_window( HWND hwnd )
 {
-    UINT raw_dpi, context = get_window_dpi_awareness_context( hwnd );
+    struct ratio dpi = {1, 1}, raw_dpi;
+    UINT context = get_window_dpi_awareness_context( hwnd );
     if (NTUSER_DPI_CONTEXT_IS_MONITOR_AWARE( context )) return get_win_monitor_dpi( hwnd, &raw_dpi );
-    return NTUSER_DPI_CONTEXT_GET_DPI( context );
+    dpi.num = NTUSER_DPI_CONTEXT_GET_DPI( context );
+    return dpi;
 }
 
 /* see GetLastActivePopup */
@@ -1735,7 +1797,7 @@ static void mirror_rect( const RECT *window_rect, RECT *rect )
  *
  * Get the window and client rectangles.
  */
-BOOL get_window_rects( HWND hwnd, enum coords_relative relative, struct window_rects *rects, UINT dpi )
+BOOL get_window_rects( HWND hwnd, enum coords_relative relative, struct window_rects *rects, struct ratio dpi )
 {
     WND *win = get_win_ptr( hwnd );
     BOOL ret = TRUE;
@@ -1766,7 +1828,7 @@ BOOL get_window_rects( HWND hwnd, enum coords_relative relative, struct window_r
     }
     if (win != WND_OTHER_PROCESS)
     {
-        UINT window_dpi = get_dpi_for_window( hwnd );
+        struct ratio window_dpi = get_dpi_for_window( hwnd );
         *rects = win->rects;
 
         switch (relative)
@@ -1866,7 +1928,7 @@ other_process:
     return ret;
 }
 
-BOOL get_window_rect_rel( HWND hwnd, enum coords_relative rel, RECT *rect, UINT dpi )
+BOOL get_window_rect_rel( HWND hwnd, enum coords_relative rel, RECT *rect, struct ratio dpi )
 {
     struct window_rects rects;
     BOOL ret = get_window_rects( hwnd, rel, &rects, dpi );
@@ -1875,12 +1937,12 @@ BOOL get_window_rect_rel( HWND hwnd, enum coords_relative rel, RECT *rect, UINT
 }
 
 /* see GetWindowRect */
-BOOL get_window_rect( HWND hwnd, RECT *rect, UINT dpi )
+BOOL get_window_rect( HWND hwnd, RECT *rect, struct ratio dpi )
 {
     return get_window_rect_rel( hwnd, COORDS_SCREEN, rect, dpi );
 }
 
-BOOL get_client_rect_rel( HWND hwnd, enum coords_relative rel, RECT *rect, UINT dpi )
+BOOL get_client_rect_rel( HWND hwnd, enum coords_relative rel, RECT *rect, struct ratio dpi )
 {
     struct window_rects rects;
     BOOL ret = get_window_rects( hwnd, rel, &rects, dpi );
@@ -1889,7 +1951,7 @@ BOOL get_client_rect_rel( HWND hwnd, enum coords_relative rel, RECT *rect, UINT
 }
 
 /* see GetClientRect */
-BOOL get_client_rect( HWND hwnd, RECT *rect, UINT dpi )
+BOOL get_client_rect( HWND hwnd, RECT *rect, struct ratio dpi )
 {
     return get_client_rect_rel( hwnd, COORDS_CLIENT, rect, dpi );
 }
@@ -1993,13 +2055,14 @@ done:
 
 static RECT get_visible_rect( HWND hwnd, BOOL shaped, UINT style, UINT ex_style, const struct window_rects *rects )
 {
-    UINT dpi = get_dpi_for_window( hwnd ), style_mask, ex_style_mask;
+    struct ratio dpi = get_dpi_for_window( hwnd );
+    UINT style_mask, ex_style_mask;
     RECT visible_rect, rect = {0};
 
     if (get_present_rect( hwnd, &rect, get_thread_dpi() )) return rect;
     if (IsRectEmpty( &rects->window ) || EqualRect( &rects->window, &rects->client ) || shaped || !decorated_mode) return rects->window;
     if (!user_driver->pGetWindowStyleMasks( hwnd, style, ex_style, &style_mask, &ex_style_mask )) return rects->window;
-    if (!NtUserAdjustWindowRect( &rect, style & style_mask, FALSE, ex_style & ex_style_mask, dpi )) return rects->window;
+    if (!adjust_window_rect( &rect, style & style_mask, FALSE, ex_style & ex_style_mask, round_dpi( dpi ) )) return rects->window;
 
     visible_rect = rects->window;
     visible_rect.left   -= rect.left;
@@ -2017,7 +2080,7 @@ static RECT get_visible_rect( HWND hwnd, BOOL shaped, UINT style, UINT ex_style,
 
 static BOOL get_surface_rect( const RECT *visible_rect, RECT *surface_rect )
 {
-    RECT virtual_rect = get_virtual_screen_rect( 0, MDT_RAW_DPI );
+    RECT virtual_rect = get_virtual_screen_rect( no_dpi, MDT_RAW_DPI );
 
     *surface_rect = *visible_rect;
 
@@ -2072,7 +2135,8 @@ static struct window_surface *get_window_surface( HWND hwnd, UINT swp_flags, BOO
     HWND parent = NtUserGetAncestor( hwnd, GA_PARENT );
     struct window_surface *new_surface;
     struct window_rects monitor_rects;
-    UINT raw_dpi, style, ex_style;
+    struct ratio raw_dpi;
+    UINT style, ex_style;
     RECT dummy;
     HRGN shape;
 
@@ -2171,7 +2235,7 @@ static BOOL apply_window_pos( HWND hwnd, HWND insert_after, UINT swp_flags, stru
     struct window_rects monitor_rects;
     WND *win;
     HWND owner_hint, surface_win = 0, toplevel;
-    UINT raw_dpi, monitor_dpi, dpi = get_thread_dpi();
+    struct ratio raw_dpi, dpi = get_thread_dpi();
     BOOL ret, is_layered, is_child, need_icons = FALSE;
     struct window_rects old_rects;
     RECT extra_rects[3];
@@ -2183,8 +2247,8 @@ static BOOL apply_window_pos( HWND hwnd, HWND insert_after, UINT swp_flags, stru
     is_layered = new_surface && new_surface->alpha_mask;
     is_child = toplevel && toplevel != hwnd;
 
-    if (is_child) monitor_dpi = get_win_monitor_dpi( toplevel, &raw_dpi );
-    else monitor_dpi = monitor_dpi_from_rect( new_rects->window, dpi, &raw_dpi );
+    if (is_child) get_win_monitor_dpi( toplevel, &raw_dpi );
+    else monitor_dpi_from_rect( new_rects->window, dpi, &raw_dpi );
 
     get_window_rects( hwnd, COORDS_PARENT, &old_rects, dpi );
     if (IsRectEmpty( &valid_rects[0] ) || is_layered) valid_rects = NULL;
@@ -2205,7 +2269,6 @@ static BOOL apply_window_pos( HWND hwnd, HWND insert_after, UINT swp_flags, stru
         req->handle        = wine_server_user_handle( hwnd );
         req->previous      = wine_server_user_handle( insert_after );
         req->swp_flags     = swp_flags;
-        req->monitor_dpi   = monitor_dpi;
         req->window        = wine_server_rectangle( new_rects->window );
         req->client        = wine_server_rectangle( new_rects->client );
         if (!EqualRect( &new_rects->window, &new_rects->visible ) || new_surface || valid_rects)
@@ -2353,7 +2416,7 @@ static BOOL apply_window_pos( HWND hwnd, HWND insert_after, UINT swp_flags, stru
     return ret;
 }
 
-static BOOL expose_window_surface( HWND hwnd, UINT flags, const RECT *rect, UINT dpi )
+static BOOL expose_window_surface( HWND hwnd, UINT flags, const RECT *rect )
 {
     struct window_surface *surface;
     struct window_rects rects;
@@ -2365,7 +2428,12 @@ static BOOL expose_window_surface( HWND hwnd, UINT flags, const RECT *rect, UINT
     rects = win->rects;
     release_win_ptr( win );
 
-    if (rect) exposed_rect = map_dpi_rect( *rect, dpi, get_dpi_for_window( hwnd ) );
+    if (rect)
+    {
+        struct ratio raw_dpi;
+        get_win_monitor_dpi( hwnd, &raw_dpi );
+        exposed_rect = map_dpi_rect( *rect, raw_dpi, get_dpi_for_window( hwnd ) );
+    }
 
     if (!surface || surface == &dummy_surface)
     {
@@ -2461,7 +2529,7 @@ int WINAPI NtUserSetWindowRgn( HWND hwnd, HRGN hrgn, BOOL redraw )
     {
         UINT swp_flags = SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_FRAMECHANGED |
             SWP_NOCLIENTSIZE | SWP_NOCLIENTMOVE;
-        UINT raw_dpi;
+        struct ratio raw_dpi;
         HRGN monitor_hrgn;
 
         if (!redraw) swp_flags |= SWP_NOREDRAW;
@@ -2653,7 +2721,7 @@ done:
  * Point is in screen coordinates.
  * Returned list must be freed by caller.
  */
-static HWND *list_children_from_point( HWND hwnd, POINT pt, UINT dpi )
+static HWND *list_children_from_point( HWND hwnd, POINT pt, struct ratio dpi )
 {
     int i, size = 128;
     HWND *list;
@@ -2699,10 +2767,10 @@ HWND window_from_point( HWND hwnd, POINT pt, INT *hittest, BOOL send_nchittest )
     int i, res;
     HWND ret, *list;
     POINT win_pt;
-    UINT dpi, raw_dpi;
+    struct ratio dpi = get_thread_dpi(), raw_dpi;
 
     if (!hwnd) hwnd = get_desktop_window();
-    if (!(dpi = get_thread_dpi())) dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
+    if (!dpi.num) dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
 
     *hittest = HTNOWHERE;
 
@@ -2856,13 +2924,77 @@ static BOOL empty_point( POINT pt )
     return pt.x == -1 && pt.y == -1;
 }
 
-/***********************************************************************
- *           NtUserGetWindowPlacement (win32u.@)
- */
-BOOL WINAPI NtUserGetWindowPlacement( HWND hwnd, WINDOWPLACEMENT *placement )
+/* When a window is a top-level window that doesn't have WS_EX_TOOLWINDOW, ptMinPosition,
+ * ptMaxPosition, and rcNormalPosition are in work area coordinates. Otherwise, they are in screen
+ * coordinates. So we need to convert the coordinates to screen coordinates. */
+static void placement_workarea_to_screen( HWND hwnd, WINDOWPLACEMENT *wp )
+{
+    DWORD style, ex_style;
+    MONITORINFO mon_info;
+
+    if (hwnd == NtUserGetDesktopWindow())
+        return;
+
+    style = get_window_long( hwnd, GWL_STYLE );
+    ex_style = get_window_long( hwnd, GWL_EXSTYLE );
+
+    if (style & WS_CHILD || ex_style & WS_EX_TOOLWINDOW)
+        return;
+
+    mon_info = monitor_info_from_rect( wp->rcNormalPosition, get_thread_dpi() );
+
+    if (!empty_point( wp->ptMinPosition ))
+    {
+        wp->ptMinPosition.x += mon_info.rcWork.left;
+        wp->ptMinPosition.y += mon_info.rcWork.top;
+    }
+
+    if (!empty_point( wp->ptMaxPosition ))
+    {
+        wp->ptMaxPosition.x += mon_info.rcWork.left;
+        wp->ptMaxPosition.y += mon_info.rcWork.top;
+    }
+
+    OffsetRect( &wp->rcNormalPosition, mon_info.rcWork.left, mon_info.rcWork.top );
+}
+
+static void placement_screen_to_workarea( HWND hwnd, WINDOWPLACEMENT *wp )
+{
+    DWORD style, ex_style;
+    MONITORINFO mon_info;
+
+    if (hwnd == NtUserGetDesktopWindow())
+        return;
+
+    style = get_window_long( hwnd, GWL_STYLE );
+    ex_style = get_window_long( hwnd, GWL_EXSTYLE );
+
+    if (style & WS_CHILD || ex_style & WS_EX_TOOLWINDOW)
+        return;
+
+    mon_info = monitor_info_from_rect( wp->rcNormalPosition, get_thread_dpi() );
+
+    if (!empty_point( wp->ptMinPosition ))
+    {
+        wp->ptMinPosition.x -= mon_info.rcWork.left;
+        wp->ptMinPosition.y -= mon_info.rcWork.top;
+    }
+
+    /* ptMaxPosition is in screen coordinates when WS_MAXIMIZE is present */
+    if (!(style & WS_MAXIMIZE) && !empty_point( wp->ptMaxPosition ))
+    {
+        wp->ptMaxPosition.x -= mon_info.rcWork.left;
+        wp->ptMaxPosition.y -= mon_info.rcWork.top;
+    }
+
+    OffsetRect( &wp->rcNormalPosition, -mon_info.rcWork.left, -mon_info.rcWork.top );
+}
+
+/* Coordinates returned in WINDOWPLACEMENT is always in screen coordinates */
+BOOL get_window_placement( HWND hwnd, WINDOWPLACEMENT *placement )
 {
     WND *win = get_win_ptr( hwnd );
-    UINT win_dpi;
+    struct ratio win_dpi;
 
     if (!win) return FALSE;
 
@@ -2937,20 +3069,32 @@ BOOL WINAPI NtUserGetWindowPlacement( HWND hwnd, WINDOWPLACEMENT *placement )
         : map_dpi_point( win->max_pos, win_dpi, get_thread_dpi() );
     placement->rcNormalPosition = map_dpi_rect( win->normal_rect, win_dpi, get_thread_dpi() );
     release_win_ptr( win );
-
-    TRACE( "%p: returning min %d,%d max %d,%d normal %s\n",
-           hwnd, placement->ptMinPosition.x, placement->ptMinPosition.y,
-           placement->ptMaxPosition.x, placement->ptMaxPosition.y,
-           wine_dbgstr_rect(&placement->rcNormalPosition) );
     return TRUE;
 }
 
+/***********************************************************************
+ *           NtUserGetWindowPlacement (win32u.@)
+ */
+BOOL WINAPI NtUserGetWindowPlacement( HWND hwnd, WINDOWPLACEMENT *placement )
+{
+    BOOL ret = get_window_placement( hwnd, placement );
+
+    if (ret)
+    {
+        placement_screen_to_workarea( hwnd, placement );
+        TRACE( "%p: returning min %d,%d max %d,%d normal %s\n",
+               hwnd, placement->ptMinPosition.x, placement->ptMinPosition.y,
+               placement->ptMaxPosition.x, placement->ptMaxPosition.y,
+               wine_dbgstr_rect(&placement->rcNormalPosition) );
+    }
+    return ret;
+}
+
 /* make sure the specified rect is visible on screen */
 static void make_rect_onscreen( RECT *rect )
 {
     MONITORINFO info = monitor_info_from_rect( *rect, get_thread_dpi() );
 
-    /* FIXME: map coordinates from rcWork to rcMonitor */
     if (rect->right <= info.rcWork.left)
     {
         rect->right += info.rcWork.left - rect->left;
@@ -2981,7 +3125,7 @@ UINT WINAPI NtUserGetInternalWindowPos( HWND hwnd, RECT *rect, POINT *pt )
     WINDOWPLACEMENT placement;
 
     placement.length = sizeof(placement);
-    if (!NtUserGetWindowPlacement( hwnd, &placement )) return 0;
+    if (!get_window_placement( hwnd, &placement )) return 0;
     if (rect) *rect = placement.rcNormalPosition;
     if (pt) *pt = placement.ptMinPosition;
     return placement.showCmd;
@@ -3010,6 +3154,7 @@ void set_window_normal_placement( HWND hwnd, RECT rect )
     }
 }
 
+/* Coordinates in WINDOWPLACEMENT should already be in screen coordinates instead of work area coordinates */
 static BOOL set_window_placement( HWND hwnd, const WINDOWPLACEMENT *wndpl, UINT flags )
 {
     WND *win = get_win_ptr( hwnd );
@@ -3077,6 +3222,8 @@ static BOOL set_window_placement( HWND hwnd, const WINDOWPLACEMENT *wndpl, UINT
  */
 BOOL WINAPI NtUserSetWindowPlacement( HWND hwnd, const WINDOWPLACEMENT *wpl )
 {
+    WINDOWPLACEMENT wp_screen;
+
     UINT flags = PLACE_MAX | PLACE_RECT;
     if (!wpl) return FALSE;
     if (wpl->length != sizeof(*wpl))
@@ -3085,7 +3232,10 @@ BOOL WINAPI NtUserSetWindowPlacement( HWND hwnd, const WINDOWPLACEMENT *wpl )
         return FALSE;
     }
     if (wpl->flags & WPF_SETMINPOSITION) flags |= PLACE_MIN;
-    return set_window_placement( hwnd, wpl, flags );
+
+    wp_screen = *wpl;
+    placement_workarea_to_screen( hwnd, &wp_screen );
+    return set_window_placement( hwnd, &wp_screen, flags );
 }
 
 /*****************************************************************************
@@ -3228,7 +3378,7 @@ INT WINAPI NtUserInternalGetWindowText( HWND hwnd, WCHAR *text, INT count )
  * Calculate the offset between the origin of the two windows. Used
  * to implement MapWindowPoints.
  */
-static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, UINT dpi, BOOL *mirrored, POINT *ret_offset )
+static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, struct ratio dpi, BOOL *mirrored, POINT *ret_offset )
 {
     WND *win;
     POINT offset;
@@ -3249,7 +3399,7 @@ static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, UINT dpi, BOOL *mi
         if (win == WND_OTHER_PROCESS) goto other_process;
         if (win != WND_DESKTOP)
         {
-            UINT raw_dpi, dpi_from = dpi ? dpi : get_win_monitor_dpi( hwnd_from, &raw_dpi );
+            struct ratio raw_dpi, dpi_from = dpi.num ? dpi : get_win_monitor_dpi( hwnd_from, &raw_dpi );
             if (win->dwExStyle & WS_EX_LAYOUTRTL)
             {
                 mirror_from = TRUE;
@@ -3286,7 +3436,7 @@ static BOOL get_windows_offset( HWND hwnd_from, HWND hwnd_to, UINT dpi, BOOL *mi
         if (win == WND_OTHER_PROCESS) goto other_process;
         if (win != WND_DESKTOP)
         {
-            UINT raw_dpi, dpi_to = dpi ? dpi : get_win_monitor_dpi( hwnd_to, &raw_dpi );
+            struct ratio raw_dpi, dpi_to = dpi.num ? dpi : get_win_monitor_dpi( hwnd_to, &raw_dpi );
             POINT pt = { 0, 0 };
             if (win->dwExStyle & WS_EX_LAYOUTRTL)
             {
@@ -3412,7 +3562,7 @@ void map_window_region( HWND from, HWND to, HRGN hrgn )
 }
 
 /* see MapWindowPoints */
-int map_window_points( HWND hwnd_from, HWND hwnd_to, POINT *points, UINT count, UINT dpi )
+int map_window_points( HWND hwnd_from, HWND hwnd_to, POINT *points, UINT count, struct ratio dpi )
 {
     BOOL mirrored;
     POINT offset;
@@ -3473,9 +3623,9 @@ static void dump_winpos_flags( UINT flags )
 static void map_dpi_winpos( WINDOWPOS *winpos )
 {
     RECT rect = {winpos->x, winpos->y, winpos->x + winpos->cx, winpos->y + winpos->cy};
-    UINT raw_dpi, dpi_from = get_thread_dpi(), dpi_to = get_dpi_for_window( winpos->hwnd );
+    struct ratio raw_dpi, dpi_from = get_thread_dpi(), dpi_to = get_dpi_for_window( winpos->hwnd );
 
-    if (!dpi_from) dpi_from = get_win_monitor_dpi( winpos->hwnd, &raw_dpi );
+    if (!dpi_from.num) dpi_from = get_win_monitor_dpi( winpos->hwnd, &raw_dpi );
     rect = map_dpi_rect( rect, dpi_from, dpi_to );
     winpos->x = rect.left;
     winpos->y = rect.top;
@@ -4557,7 +4707,7 @@ static UINT window_min_maximize( HWND hwnd, UINT cmd, RECT *rect )
     TRACE( "%p %u\n", hwnd, cmd );
 
     wpl.length = sizeof(wpl);
-    NtUserGetWindowPlacement( hwnd, &wpl );
+    get_window_placement( hwnd, &wpl );
 
     if (call_hooks( WH_CBT, HCBT_MINMAX, (WPARAM)hwnd, cmd, 0 ))
         return SWP_NOSIZE | SWP_NOMOVE;
@@ -5615,15 +5765,15 @@ static void fix_cs_coordinates( CREATESTRUCTW *cs, INT *sw )
 /***********************************************************************
  *           map_dpi_create_struct
  */
-static void map_dpi_create_struct( CREATESTRUCTW *cs, UINT dpi_to )
+static void map_dpi_create_struct( CREATESTRUCTW *cs, struct ratio dpi_to )
 {
     RECT rect = {cs->x, cs->y, cs->x + cs->cx, cs->y + cs->cy};
-    UINT dpi_from = get_thread_dpi();
+    struct ratio dpi_from = get_thread_dpi();
 
-    if (!dpi_from || !dpi_to)
+    if (!dpi_from.num || !dpi_to.num)
     {
-        UINT raw_dpi, mon_dpi = monitor_dpi_from_rect( rect, get_thread_dpi(), &raw_dpi );
-        if (!dpi_from) dpi_from = mon_dpi;
+        struct ratio raw_dpi, mon_dpi = monitor_dpi_from_rect( rect, get_thread_dpi(), &raw_dpi );
+        if (!dpi_from.num) dpi_from = mon_dpi;
         else dpi_to = mon_dpi;
     }
 
@@ -5644,7 +5794,7 @@ HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name,
                                   DWORD flags, HINSTANCE instance, const WCHAR *class, BOOL ansi )
 {
     WCHAR base_nameW[MAX_ATOM_LEN + 1];
-    UINT win_dpi, context;
+    struct ratio win_dpi;
     struct window_surface *surface;
     struct window_rects new_rects;
     CBT_CREATEWNDW cbtc;
@@ -5652,6 +5802,7 @@ HWND WINAPI NtUserCreateWindowEx( DWORD ex_style, UNICODE_STRING *class_name,
     CREATESTRUCTW cs;
     INT sw = SW_SHOW;
     RECT surface_rect;
+    UINT context;
     WND *win;
 
     TRACE( "ex_style %#x, class_name %s, version %s, window_name %s, style %#x, x %u, y %u, cx %u, cy %u, "
@@ -6000,13 +6151,13 @@ static BOOL set_raw_window_pos( HWND hwnd, RECT rect, UINT flags, BOOL internal
     return NtUserSetWindowPos( hwnd, 0, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, flags );
 }
 
-BOOL get_present_rect( HWND hwnd, RECT *rect, UINT dpi )
+BOOL get_present_rect( HWND hwnd, RECT *rect, struct ratio dpi )
 {
-    UINT dpi_from = get_dpi_for_window( hwnd );
+    struct ratio dpi_from = get_dpi_for_window( hwnd );
     WND *win;
 
     if (!(win = get_win_ptr( hwnd )) || win == WND_OTHER_PROCESS || win == WND_DESKTOP) return FALSE;
-    if (dpi != -1) *rect = map_dpi_rect( win->present_rect, dpi_from, dpi );
+    if (dpi.num != (unsigned short)-1) *rect = map_dpi_rect( win->present_rect, dpi_from, dpi );
     else *rect = map_rect_virt_to_raw( win->present_rect, dpi_from );
     release_win_ptr( win );
 
@@ -6028,7 +6179,7 @@ ULONG_PTR WINAPI NtUserCallHwnd( HWND hwnd, DWORD code )
         return set_foreground_window( hwnd, FALSE, TRUE );
 
     case NtUserCallHwnd_GetDpiForWindow:
-        return get_dpi_for_window( hwnd );
+        return get_dpi_for_window( hwnd ).num;
 
     case NtUserCallHwnd_GetLastActivePopup:
         return (ULONG_PTR)get_last_active_popup( hwnd );
@@ -6135,17 +6286,20 @@ ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
     case NtUserCallHwndParam_GetWindowRect:
     {
         struct get_window_rects_params *params = (void *)param;
-        return get_window_rect( hwnd, params->rect, params->dpi );
+        struct ratio dpi = {params->dpi, 1};
+        return get_window_rect( hwnd, params->rect, dpi );
     }
     case NtUserCallHwndParam_GetClientRect:
     {
         struct get_window_rects_params *params = (void *)param;
-        return get_client_rect( hwnd, params->rect, params->dpi );
+        struct ratio dpi = {params->dpi, 1};
+        return get_client_rect( hwnd, params->rect, dpi );
     }
     case NtUserCallHwndParam_GetPresentRect:
     {
         struct get_window_rects_params *params = (void *)param;
-        return get_present_rect( hwnd, params->rect, params->dpi );
+        struct ratio dpi = {params->dpi, 1};
+        return get_present_rect( hwnd, params->rect, dpi );
     }
 
     case NtUserCallHwndParam_GetWindowRelative:
@@ -6161,10 +6315,11 @@ ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
         return is_child( hwnd, UlongToHandle(param) );
 
     case NtUserCallHwndParam_MapWindowPoints:
-        {
-            struct map_window_points_params *params = (void *)param;
-            return map_window_points( hwnd, params->hwnd_to, params->points, params->count, params->dpi );
-        }
+    {
+        struct map_window_points_params *params = (void *)param;
+        struct ratio dpi = {params->dpi, 1};
+        return map_window_points( hwnd, params->hwnd_to, params->points, params->count, dpi );
+    }
 
     case NtUserCallHwndParam_MirrorRgn:
         return mirror_window_region( hwnd, UlongToHandle(param) );
@@ -6185,19 +6340,19 @@ ULONG_PTR WINAPI NtUserCallHwndParam( HWND hwnd, DWORD_PTR param, DWORD code )
     case NtUserCallHwndParam_SendHardwareInput:
     {
         struct send_hardware_input_params *params = (void *)param;
-        return send_hardware_message( hwnd, params->flags, params->input, params->lparam );
+        return send_hardware_input( hwnd, params->flags, params->input, params->lparam );
     }
 
     case NtUserCallHwndParam_ExposeWindowSurface:
     {
         struct expose_window_surface_params *params = (void *)param;
-        return expose_window_surface( hwnd, params->flags, params->whole ? NULL : &params->rect, params->dpi );
+        return expose_window_surface( hwnd, params->flags, params->whole ? NULL : &params->rect );
     }
 
     case NtUserCallHwndParam_GetWinMonitorDpi:
     {
-        UINT raw_dpi, dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
-        return param == MDT_EFFECTIVE_DPI ? dpi : raw_dpi;
+        struct ratio raw_dpi, dpi = get_win_monitor_dpi( hwnd, &raw_dpi );
+        return param == MDT_EFFECTIVE_DPI ? round_dpi( dpi ) : round_dpi( raw_dpi );
     }
 
     case NtUserCallHwndParam_SetRawWindowPos:
diff --git a/dlls/windows.storage/Makefile.in b/dlls/windows.storage/Makefile.in
index bb446a2be991..adc8597dd11c 100644
--- a/dlls/windows.storage/Makefile.in
+++ b/dlls/windows.storage/Makefile.in
@@ -2,6 +2,8 @@ MODULE  = windows.storage.dll
 IMPORTS = shell32 combase
 
 SOURCES = \
+	async.c \
+	async_private.idl \
 	classes.idl \
 	main.c \
 	streams.c
diff --git a/dlls/windows.storage/async.c b/dlls/windows.storage/async.c
new file mode 100644
index 000000000000..6a06a8577e88
--- /dev/null
+++ b/dlls/windows.storage/async.c
@@ -0,0 +1,542 @@
+/* WinRT IAsync* implementation
+ *
+ * Copyright 2022 Bernhard Kölbl for CodeWeavers
+ * Copyright 2022 Rémi Bernon for CodeWeavers
+ * Copyright 2026 Conor McCarthy 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
+ */
+
+#define WIDL_using_Wine_Internal
+#include "private.h"
+#include "initguid.h"
+
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(storage);
+
+#define Closed 4
+#define HANDLER_NOT_SET ((void *)~(ULONG_PTR)0)
+
+struct async_info
+{
+    IAsyncInfoImpl IAsyncInfoImpl_iface;
+    IAsyncInfo IAsyncInfo_iface;
+    IInspectable *IInspectable_outer;
+    LONG ref;
+
+    async_operation_callback callback;
+    TP_WORK *async_run_work;
+    IUnknown *invoker;
+    IUnknown *param;
+
+    CRITICAL_SECTION cs;
+    IAsyncOperationCompletedHandlerImpl *handler;
+    PROPVARIANT result;
+    AsyncStatus status;
+    HRESULT hr;
+};
+
+static inline struct async_info *impl_from_IAsyncInfoImpl( IAsyncInfoImpl *iface )
+{
+    return CONTAINING_RECORD( iface, struct async_info, IAsyncInfoImpl_iface );
+}
+
+static HRESULT WINAPI async_impl_QueryInterface( IAsyncInfoImpl *iface, REFIID iid, void **out )
+{
+    struct async_info *impl = impl_from_IAsyncInfoImpl( iface );
+
+    TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out );
+
+    if (IsEqualGUID( iid, &IID_IUnknown ) ||
+        IsEqualGUID( iid, &IID_IInspectable ) ||
+        IsEqualGUID( iid, &IID_IAgileObject ) ||
+        IsEqualGUID( iid, &IID_IAsyncInfoImpl ))
+    {
+        IInspectable_AddRef( (*out = &impl->IAsyncInfoImpl_iface) );
+        return S_OK;
+    }
+
+    if (IsEqualGUID( iid, &IID_IAsyncInfo ))
+    {
+        IInspectable_AddRef( (*out = &impl->IAsyncInfo_iface) );
+        return S_OK;
+    }
+
+    FIXME( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI async_impl_AddRef( IAsyncInfoImpl *iface )
+{
+    struct async_info *impl = impl_from_IAsyncInfoImpl( iface );
+    ULONG ref = InterlockedIncrement( &impl->ref );
+    TRACE( "iface %p, ref %lu.\n", iface, ref );
+    return ref;
+}
+
+static ULONG WINAPI async_impl_Release( IAsyncInfoImpl *iface )
+{
+    struct async_info *impl = impl_from_IAsyncInfoImpl( iface );
+    ULONG ref = InterlockedDecrement( &impl->ref );
+    TRACE( "iface %p, ref %lu.\n", iface, ref );
+
+    if (!ref)
+    {
+        if (impl->handler && impl->handler != HANDLER_NOT_SET) IAsyncOperationCompletedHandlerImpl_Release( impl->handler );
+        IAsyncInfo_Close( &impl->IAsyncInfo_iface );
+        if (impl->param) IUnknown_Release( impl->param );
+        if (impl->invoker) IUnknown_Release( impl->invoker );
+        PropVariantClear( &impl->result );
+        impl->cs.DebugInfo->Spare[0] = 0;
+        DeleteCriticalSection( &impl->cs );
+        free( impl );
+    }
+
+    return ref;
+}
+
+static HRESULT WINAPI async_impl_put_Completed( IAsyncInfoImpl *iface, IAsyncOperationCompletedHandlerImpl *handler )
+{
+    struct async_info *impl = impl_from_IAsyncInfoImpl( iface );
+    HRESULT hr = S_OK;
+
+    TRACE( "iface %p, handler %p.\n", iface, handler );
+
+    EnterCriticalSection( &impl->cs );
+    if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL;
+    else if (impl->handler != HANDLER_NOT_SET) hr = E_ILLEGAL_DELEGATE_ASSIGNMENT;
+    else if ((impl->handler = handler))
+    {
+        IAsyncOperationCompletedHandlerImpl_AddRef( impl->handler );
+
+        if (impl->status > Started)
+        {
+            IInspectable *operation = impl->IInspectable_outer;
+            AsyncStatus status = impl->status;
+            impl->handler = NULL; /* Prevent concurrent invoke. */
+            LeaveCriticalSection( &impl->cs );
+
+            IAsyncOperationCompletedHandlerImpl_Invoke( handler, operation, status );
+            IAsyncOperationCompletedHandlerImpl_Release( handler );
+
+            return S_OK;
+        }
+    }
+    LeaveCriticalSection( &impl->cs );
+
+    return hr;
+}
+
+static HRESULT WINAPI async_impl_get_Completed( IAsyncInfoImpl *iface, IAsyncOperationCompletedHandlerImpl **handler )
+{
+    struct async_info *impl = impl_from_IAsyncInfoImpl( iface );
+    HRESULT hr = S_OK;
+
+    TRACE( "iface %p, handler %p.\n", iface, handler );
+
+    EnterCriticalSection( &impl->cs );
+    if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL;
+    if (impl->handler == NULL || impl->handler == HANDLER_NOT_SET) *handler = NULL;
+    else IAsyncOperationCompletedHandlerImpl_AddRef( (*handler = impl->handler) );
+    LeaveCriticalSection( &impl->cs );
+
+    return hr;
+}
+
+static HRESULT WINAPI async_impl_get_Result( IAsyncInfoImpl *iface, PROPVARIANT *result )
+{
+    struct async_info *impl = impl_from_IAsyncInfoImpl( iface );
+    HRESULT hr = E_ILLEGAL_METHOD_CALL;
+
+    TRACE( "iface %p, result %p.\n", iface, result );
+
+    EnterCriticalSection( &impl->cs );
+    if (impl->status == Completed || impl->status == Error)
+    {
+        PropVariantCopy( result, &impl->result );
+        hr = impl->hr;
+    }
+    LeaveCriticalSection( &impl->cs );
+
+    return hr;
+}
+
+static BOOL async_info_complete( struct async_info *impl, BOOL called_async )
+{
+    IInspectable *operation = impl->IInspectable_outer;
+    PROPVARIANT result = {0};
+    HRESULT hr;
+
+    hr = impl->callback( impl->invoker, impl->param, &result, called_async );
+    if (!called_async && hr == STATUS_PENDING) return FALSE;
+
+    EnterCriticalSection( &impl->cs );
+    if (impl->status != Closed) impl->status = FAILED(hr) ? Error : Completed;
+    PropVariantCopy( &impl->result, &result );
+    impl->hr = hr;
+
+    if (impl->handler != NULL && impl->handler != HANDLER_NOT_SET)
+    {
+        IAsyncOperationCompletedHandlerImpl *handler = impl->handler;
+        AsyncStatus status = impl->status;
+        impl->handler = NULL; /* Prevent concurrent invoke. */
+        LeaveCriticalSection( &impl->cs );
+
+        IAsyncOperationCompletedHandlerImpl_Invoke( handler, operation, status );
+        IAsyncOperationCompletedHandlerImpl_Release( handler );
+    }
+    else LeaveCriticalSection( &impl->cs );
+
+    /* release refcount acquired in Start */
+    IInspectable_Release( operation );
+
+    PropVariantClear( &result );
+    return TRUE;
+}
+
+static HRESULT WINAPI async_impl_Start( IAsyncInfoImpl *iface )
+{
+    struct async_info *impl = impl_from_IAsyncInfoImpl( iface );
+
+    TRACE( "iface %p.\n", iface );
+
+    /* keep the async alive in the callback */
+    IInspectable_AddRef( impl->IInspectable_outer );
+    if (!async_info_complete( impl, FALSE )) SubmitThreadpoolWork( impl->async_run_work );
+
+    return S_OK;
+}
+
+static const struct IAsyncInfoImplVtbl async_impl_vtbl =
+{
+    /* IUnknown methods */
+    async_impl_QueryInterface,
+    async_impl_AddRef,
+    async_impl_Release,
+    /* IAsyncInfoImpl */
+    async_impl_put_Completed,
+    async_impl_get_Completed,
+    async_impl_get_Result,
+    async_impl_Start,
+};
+
+DEFINE_IINSPECTABLE_OUTER( async_info, IAsyncInfo, struct async_info, IInspectable_outer )
+
+static HRESULT WINAPI async_info_get_Id( IAsyncInfo *iface, UINT32 *id )
+{
+    struct async_info *impl = impl_from_IAsyncInfo( iface );
+    HRESULT hr = S_OK;
+
+    TRACE( "iface %p, id %p.\n", iface, id );
+
+    EnterCriticalSection( &impl->cs );
+    if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL;
+    *id = 1;
+    LeaveCriticalSection( &impl->cs );
+
+    return hr;
+}
+
+static HRESULT WINAPI async_info_get_Status( IAsyncInfo *iface, AsyncStatus *status )
+{
+    struct async_info *impl = impl_from_IAsyncInfo( iface );
+    HRESULT hr = S_OK;
+
+    TRACE( "iface %p, status %p.\n", iface, status );
+
+    EnterCriticalSection( &impl->cs );
+    if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL;
+    *status = impl->status;
+    LeaveCriticalSection( &impl->cs );
+
+    return hr;
+}
+
+static HRESULT WINAPI async_info_get_ErrorCode( IAsyncInfo *iface, HRESULT *error_code )
+{
+    struct async_info *impl = impl_from_IAsyncInfo( iface );
+    HRESULT hr = S_OK;
+
+    TRACE( "iface %p, error_code %p.\n", iface, error_code );
+
+    EnterCriticalSection( &impl->cs );
+    if (impl->status == Closed) *error_code = hr = E_ILLEGAL_METHOD_CALL;
+    else *error_code = impl->hr;
+    LeaveCriticalSection( &impl->cs );
+
+    return hr;
+}
+
+static HRESULT WINAPI async_info_Cancel( IAsyncInfo *iface )
+{
+    struct async_info *impl = impl_from_IAsyncInfo( iface );
+    HRESULT hr = S_OK;
+
+    TRACE( "iface %p.\n", iface );
+
+    EnterCriticalSection( &impl->cs );
+    if (impl->status == Closed) hr = E_ILLEGAL_METHOD_CALL;
+    else if (impl->status == Started) impl->status = Canceled;
+    LeaveCriticalSection( &impl->cs );
+
+    return hr;
+}
+
+static HRESULT WINAPI async_info_Close( IAsyncInfo *iface )
+{
+    struct async_info *impl = impl_from_IAsyncInfo( iface );
+    HRESULT hr = S_OK;
+
+    TRACE( "iface %p.\n", iface );
+
+    EnterCriticalSection( &impl->cs );
+    if (impl->status == Started)
+        hr = E_ILLEGAL_STATE_CHANGE;
+    else if (impl->status != Closed)
+    {
+        CloseThreadpoolWork( impl->async_run_work );
+        impl->async_run_work = NULL;
+        impl->status = Closed;
+    }
+    LeaveCriticalSection( &impl->cs );
+
+    return hr;
+}
+
+static const struct IAsyncInfoVtbl async_info_vtbl =
+{
+    /* IUnknown methods */
+    async_info_QueryInterface,
+    async_info_AddRef,
+    async_info_Release,
+    /* IInspectable methods */
+    async_info_GetIids,
+    async_info_GetRuntimeClassName,
+    async_info_GetTrustLevel,
+    /* IAsyncInfo */
+    async_info_get_Id,
+    async_info_get_Status,
+    async_info_get_ErrorCode,
+    async_info_Cancel,
+    async_info_Close,
+};
+
+static void CALLBACK async_info_callback( TP_CALLBACK_INSTANCE *instance, void *iface, TP_WORK *work )
+{
+    struct async_info *impl = impl_from_IAsyncInfoImpl( iface );
+
+    async_info_complete( impl, TRUE );
+}
+
+static HRESULT async_info_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback,
+                                  IInspectable *outer, IAsyncInfoImpl **out )
+{
+    struct async_info *impl;
+    HRESULT hr;
+
+    if (!(impl = calloc( 1, sizeof(struct async_info) ))) return E_OUTOFMEMORY;
+    impl->IAsyncInfoImpl_iface.lpVtbl = &async_impl_vtbl;
+    impl->IAsyncInfo_iface.lpVtbl = &async_info_vtbl;
+    impl->IInspectable_outer = outer;
+    impl->ref = 1;
+
+    impl->callback = callback;
+    impl->handler = HANDLER_NOT_SET;
+    impl->status = Started;
+    if (!(impl->async_run_work = CreateThreadpoolWork( async_info_callback, &impl->IAsyncInfoImpl_iface, NULL )))
+    {
+        hr = HRESULT_FROM_WIN32( GetLastError() );
+        free( impl );
+        return hr;
+    }
+
+    if ((impl->invoker = invoker)) IUnknown_AddRef( impl->invoker );
+    if ((impl->param = param)) IUnknown_AddRef( impl->param );
+
+    InitializeCriticalSectionEx( &impl->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO );
+    impl->cs.DebugInfo->Spare[0] = (DWORD_PTR)( __FILE__ ": async_info.cs" );
+
+    *out = &impl->IAsyncInfoImpl_iface;
+    return S_OK;
+}
+
+#define DEFINE_IASYNCOPERATIONWITHPROGRESS( iface_type, progress_type, completed_type, cs_type_str, impl_type,      \
+        result_type, propvar_type, propvar_name, creation_fn )                                                      \
+static inline struct impl_type *impl_from_##iface_type( iface_type *iface )                                         \
+{                                                                                                                   \
+    return CONTAINING_RECORD( iface, struct impl_type, iface_type##_iface );                                        \
+}                                                                                                                   \
+static HRESULT WINAPI impl_type##_QueryInterface( iface_type *iface, REFIID iid, void **out )                       \
+{                                                                                                                   \
+    struct impl_type *impl = impl_from_##iface_type( iface );                                                       \
+                                                                                                                    \
+    TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out );                                       \
+                                                                                                                    \
+    if (IsEqualGUID( iid, &IID_IUnknown ) ||                                                                        \
+        IsEqualGUID( iid, &IID_IInspectable ) ||                                                                    \
+        IsEqualGUID( iid, &IID_IAgileObject ) ||                                                                    \
+        IsEqualGUID( iid, &IID_##iface_type ))                                                                      \
+    {                                                                                                               \
+        IInspectable_AddRef( (*out = &impl->iface_type##_iface) );                                                  \
+        return S_OK;                                                                                                \
+    }                                                                                                               \
+                                                                                                                    \
+    return IAsyncInfoImpl_QueryInterface( impl->IAsyncInfoImpl_inner, iid, out );                                   \
+}                                                                                                                   \
+static ULONG WINAPI impl_type##_AddRef( iface_type *iface )                                                         \
+{                                                                                                                   \
+    struct impl_type *impl = impl_from_##iface_type( iface );                                                       \
+    ULONG ref = InterlockedIncrement( &impl->ref );                                                                 \
+    TRACE( "iface %p, ref %lu.\n", iface, ref );                                                                    \
+    return ref;                                                                                                     \
+}                                                                                                                   \
+static ULONG WINAPI impl_type##_Release( iface_type *iface )                                                        \
+{                                                                                                                   \
+    struct impl_type *impl = impl_from_##iface_type( iface );                                                       \
+    ULONG ref = InterlockedDecrement( &impl->ref );                                                                 \
+    TRACE( "iface %p, ref %lu.\n", iface, ref );                                                                    \
+                                                                                                                    \
+    if (!ref)                                                                                                       \
+    {                                                                                                               \
+        /* guard against re-entry if inner releases an outer iface */                                               \
+        InterlockedIncrement( &impl->ref );                                                                         \
+        IAsyncInfoImpl_Release( impl->IAsyncInfoImpl_inner );                                                       \
+        free( impl );                                                                                               \
+    }                                                                                                               \
+                                                                                                                    \
+    return ref;                                                                                                     \
+}                                                                                                                   \
+static HRESULT WINAPI impl_type##_GetIids( iface_type *iface, ULONG *iid_count, IID **iids )                        \
+{                                                                                                                   \
+    FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids );                                     \
+    return E_NOTIMPL;                                                                                               \
+}                                                                                                                   \
+static HRESULT WINAPI impl_type##_GetRuntimeClassName( iface_type *iface, HSTRING *class_name )                     \
+{                                                                                                                   \
+    return WindowsCreateString( L"Windows.Foundation.IAsyncOperationWithProgress`2<"cs_type_str">",                 \
+                                ARRAY_SIZE(L"Windows.Foundation.IAsyncOperationWithProgress`2<"cs_type_str">"),     \
+                                class_name );                                                                       \
+}                                                                                                                   \
+static HRESULT WINAPI impl_type##_GetTrustLevel( iface_type *iface, TrustLevel *trust_level )                       \
+{                                                                                                                   \
+    FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level );                                                \
+    return E_NOTIMPL;                                                                                               \
+}                                                                                                                   \
+static HRESULT WINAPI impl_type##_put_Progress( iface_type *iface, progress_type *handler )                         \
+{                                                                                                                   \
+    FIXME( "iface %p, handler %p stub!\n", iface, handler );                                                        \
+    return E_NOTIMPL;                                                                                               \
+}                                                                                                                   \
+static HRESULT WINAPI impl_type##_get_Progress( iface_type *iface, progress_type **handler )                        \
+{                                                                                                                   \
+    FIXME( "iface %p, handler %p stub!\n", iface, handler );                                                        \
+    return E_NOTIMPL;                                                                                               \
+}                                                                                                                   \
+static HRESULT WINAPI impl_type##_put_Completed( iface_type *iface, completed_type *typed_handler )                 \
+{                                                                                                                   \
+    IAsyncOperationCompletedHandlerImpl *handler = (IAsyncOperationCompletedHandlerImpl *)typed_handler;            \
+    struct impl_type *impl = impl_from_##iface_type( iface );                                                       \
+    TRACE( "iface %p, handler %p.\n", iface, handler );                                                             \
+    return IAsyncInfoImpl_put_Completed( impl->IAsyncInfoImpl_inner, handler );                                     \
+}                                                                                                                   \
+static HRESULT WINAPI impl_type##_get_Completed( iface_type *iface, completed_type **typed_handler )                \
+{                                                                                                                   \
+    IAsyncOperationCompletedHandlerImpl **handler = (IAsyncOperationCompletedHandlerImpl **)typed_handler;          \
+    struct impl_type *impl = impl_from_##iface_type( iface );                                                       \
+    TRACE( "iface %p, handler %p.\n", iface, handler );                                                             \
+    return IAsyncInfoImpl_get_Completed( impl->IAsyncInfoImpl_inner, handler );                                     \
+}                                                                                                                   \
+static HRESULT WINAPI impl_type##_GetResults( iface_type *iface, result_type *results )                             \
+{                                                                                                                   \
+    struct impl_type *impl = impl_from_##iface_type( iface );                                                       \
+    PROPVARIANT result = {.vt = propvar_type};                                                                      \
+    HRESULT hr;                                                                                                     \
+                                                                                                                    \
+    TRACE( "iface %p, results %p.\n", iface, results );                                                             \
+                                                                                                                    \
+    hr = IAsyncInfoImpl_get_Result( impl->IAsyncInfoImpl_inner, &result );                                          \
+                                                                                                                    \
+    *results = (result_type)result.propvar_name;                                                                    \
+    if (propvar_type == VT_UNKNOWN && result.punkVal) IUnknown_AddRef( result.punkVal );                            \
+    PropVariantClear( &result );                                                                                    \
+    return hr;                                                                                                      \
+}                                                                                                                   \
+static const struct iface_type##Vtbl impl_type##_vtbl =                                                             \
+{                                                                                                                   \
+    /* IUnknown methods */                                                                                          \
+    impl_type##_QueryInterface,                                                                                     \
+    impl_type##_AddRef,                                                                                             \
+    impl_type##_Release,                                                                                            \
+    /* IInspectable methods */                                                                                      \
+    impl_type##_GetIids,                                                                                            \
+    impl_type##_GetRuntimeClassName,                                                                                \
+    impl_type##_GetTrustLevel,                                                                                      \
+    /* IAsyncOperationWithProgress<result_type,prog_type> */                                                        \
+    impl_type##_put_Progress,                                                                                       \
+    impl_type##_get_Progress,                                                                                       \
+    impl_type##_put_Completed,                                                                                      \
+    impl_type##_get_Completed,                                                                                      \
+    impl_type##_GetResults,                                                                                         \
+};                                                                                                                  \
+HRESULT creation_fn( IUnknown *invoker, IUnknown *param,                                                            \
+        async_operation_callback callback, iface_type **out )                                                       \
+{                                                                                                                   \
+    struct impl_type *impl;                                                                                         \
+    HRESULT hr;                                                                                                     \
+                                                                                                                    \
+    *out = NULL;                                                                                                    \
+    if (!(impl = calloc( 1, sizeof(*impl) ))) return E_OUTOFMEMORY;                                                 \
+    impl->iface_type##_iface.lpVtbl = &impl_type##_vtbl;                                                            \
+    impl->ref = 1;                                                                                                  \
+                                                                                                                    \
+    if (FAILED(hr = async_info_create( invoker, param, callback,                                                    \
+            (IInspectable *)&impl->iface_type##_iface, &impl->IAsyncInfoImpl_inner ))                               \
+            || FAILED(hr = IAsyncInfoImpl_Start( impl->IAsyncInfoImpl_inner )))                                     \
+    {                                                                                                               \
+        if (impl->IAsyncInfoImpl_inner) IAsyncInfoImpl_Release( impl->IAsyncInfoImpl_inner );                       \
+        free( impl );                                                                                               \
+        return hr;                                                                                                  \
+    }                                                                                                               \
+                                                                                                                    \
+    *out = &impl->iface_type##_iface;                                                                               \
+    TRACE( "created IAsyncOperationWithProgress %p.\n", *out );                                                      \
+    return S_OK;                                                                                                    \
+}                                                                                                                   \
+
+struct async_buffer_uint32
+{
+    IAsyncOperationWithProgress_IBuffer_UINT32 IAsyncOperationWithProgress_IBuffer_UINT32_iface;
+    IAsyncInfoImpl *IAsyncInfoImpl_inner;
+    LONG ref;
+};
+
+DEFINE_IASYNCOPERATIONWITHPROGRESS( IAsyncOperationWithProgress_IBuffer_UINT32, \
+        IAsyncOperationProgressHandler_IBuffer_UINT32, \
+        IAsyncOperationWithProgressCompletedHandler_IBuffer_UINT32, \
+        "IBuffer,UInt32", async_buffer_uint32, IBuffer*, VT_UNKNOWN, punkVal, async_operation_buffer_uint32_create )
+
+struct async_uint32_uint32
+{
+    IAsyncOperationWithProgress_UINT32_UINT32 IAsyncOperationWithProgress_UINT32_UINT32_iface;
+    IAsyncInfoImpl *IAsyncInfoImpl_inner;
+    LONG ref;
+};
+
+DEFINE_IASYNCOPERATIONWITHPROGRESS( IAsyncOperationWithProgress_UINT32_UINT32, \
+        IAsyncOperationProgressHandler_UINT32_UINT32, \
+        IAsyncOperationWithProgressCompletedHandler_UINT32_UINT32, \
+        "UInt32,UInt32", async_uint32_uint32, UINT32, VT_UI4, ulVal, async_operation_uint32_uint32_create )
diff --git a/dlls/windows.storage/async_private.idl b/dlls/windows.storage/async_private.idl
new file mode 100644
index 000000000000..40ea63ca4c10
--- /dev/null
+++ b/dlls/windows.storage/async_private.idl
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2022 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
+ */
+
+#pragma makedep header
+
+#ifdef __WIDL__
+#pragma winrt ns_prefix
+#endif
+
+import "propidl.idl";
+import "inspectable.idl";
+import "asyncinfo.idl";
+import "windowscontracts.idl";
+
+namespace Wine.Internal {
+    /* type-pruning version of AsyncOperationCompletedHandlerImpl<T> */
+    delegate HRESULT AsyncOperationCompletedHandlerImpl([in] IInspectable *async, [in] AsyncStatus status);
+
+    [
+        uuid(d81ab70d-82e0-481c-983d-401225d98a2c)
+    ]
+    interface IAsyncInfoImpl : IUnknown
+    {
+        [propput] HRESULT Completed([in] AsyncOperationCompletedHandlerImpl *handler);
+        [propget] HRESULT Completed([out, retval] AsyncOperationCompletedHandlerImpl **handler);
+        [propget] HRESULT Result([out, retval] PROPVARIANT *result);
+        HRESULT Start();
+    }
+
+    typedef HRESULT (*async_operation_callback)( IUnknown *invoker, IUnknown *param, PROPVARIANT *result, BOOL called_async );
+}
diff --git a/dlls/windows.storage/classes.idl b/dlls/windows.storage/classes.idl
index d64d1c45ec8e..13f056fcc8f8 100644
--- a/dlls/windows.storage/classes.idl
+++ b/dlls/windows.storage/classes.idl
@@ -31,4 +31,5 @@ import "windows.storage.idl";
 
 namespace Windows.Storage.Streams {
     runtimeclass RandomAccessStreamReference;
+    runtimeclass InMemoryRandomAccessStream;
 }
diff --git a/dlls/windows.storage/main.c b/dlls/windows.storage/main.c
index 390f2dec7423..909817a263d4 100644
--- a/dlls/windows.storage/main.c
+++ b/dlls/windows.storage/main.c
@@ -38,6 +38,8 @@ HRESULT WINAPI DllGetActivationFactory( HSTRING classid, IActivationFactory **fa
 
     if (!wcscmp( buffer, RuntimeClass_Windows_Storage_Streams_RandomAccessStreamReference ))
         IActivationFactory_QueryInterface( random_access_stream_reference_factory, &IID_IActivationFactory, (void **)factory );
+    if (!wcscmp( buffer, RuntimeClass_Windows_Storage_Streams_InMemoryRandomAccessStream ))
+        IActivationFactory_QueryInterface( memory_stream_activation_factory, &IID_IActivationFactory, (void **)factory );
 
     if (*factory) return S_OK;
     return CLASS_E_CLASSNOTAVAILABLE;
diff --git a/dlls/windows.storage/private.h b/dlls/windows.storage/private.h
index 62c9d226d07c..21ef87124e04 100644
--- a/dlls/windows.storage/private.h
+++ b/dlls/windows.storage/private.h
@@ -38,8 +38,16 @@
 #define WIDL_using_Windows_Storage_Streams
 #include "windows.storage.h"
 #include "windows.storage.streams.h"
+#include "robuffer.h"
+#include "async_private.h"
+
+HRESULT async_operation_buffer_uint32_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback,
+        IAsyncOperationWithProgress_IBuffer_UINT32 **out );
+HRESULT async_operation_uint32_uint32_create( IUnknown *invoker, IUnknown *param, async_operation_callback callback,
+        IAsyncOperationWithProgress_UINT32_UINT32 **out );
 
 extern IActivationFactory *random_access_stream_reference_factory;
+extern IActivationFactory *memory_stream_activation_factory;
 
 #define DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from, iface_mem, expr )             \
     static inline impl_type *impl_from( iface_type *iface )                                        \
@@ -78,5 +86,50 @@ extern IActivationFactory *random_access_stream_reference_factory;
     }
 #define DEFINE_IINSPECTABLE( pfx, iface_type, impl_type, base_iface )                              \
     DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, &impl->base_iface )
+#define DEFINE_IINSPECTABLE_OUTER( pfx, iface_type, impl_type, outer_iface )                       \
+    DEFINE_IINSPECTABLE_( pfx, iface_type, impl_type, impl_from_##iface_type, iface_type##_iface, impl->outer_iface )
+
+#define DEFINE_ASYNC_PARAMS( type )                                                                \
+    static struct type *type##_from_IUnknown( IUnknown *iface )                                    \
+    {                                                                                              \
+        return CONTAINING_RECORD( iface, struct type, IUnknown_iface );                            \
+    }                                                                                              \
+    static HRESULT WINAPI type##_QueryInterface( IUnknown *iface, REFIID iid, void **out )         \
+    {                                                                                              \
+        if (IsEqualIID( iid, &IID_IUnknown ))                                                      \
+        {                                                                                          \
+            IUnknown_AddRef( iface );                                                              \
+            *out = iface;                                                                          \
+            return S_OK;                                                                           \
+        }                                                                                          \
+        *out = NULL;                                                                               \
+        return E_NOINTERFACE;                                                                      \
+    }                                                                                              \
+    static ULONG WINAPI type##_AddRef( IUnknown *iface )                                           \
+    {                                                                                              \
+        struct type *object = type##_from_IUnknown( iface );                                       \
+        return InterlockedIncrement( &object->ref );                                               \
+    }                                                                                              \
+    static ULONG WINAPI type##_Release( IUnknown *iface )                                          \
+    {                                                                                              \
+        struct type *object = type##_from_IUnknown( iface );                                       \
+        ULONG ref = InterlockedDecrement( &object->ref );                                          \
+        if (!ref) type##_##destroy( object );                                                      \
+        return ref;                                                                                \
+    }                                                                                              \
+    static const IUnknownVtbl type##_vtbl =                                                        \
+    {                                                                                              \
+        type##_QueryInterface,                                                                     \
+        type##_AddRef,                                                                             \
+        type##_Release,                                                                            \
+    };                                                                                             \
+    static struct type *type##_alloc(void)                                                         \
+    {                                                                                              \
+        struct type *object;                                                                       \
+        if (!(object = calloc( 1, sizeof(*object) ))) return NULL;                                 \
+        object->IUnknown_iface.lpVtbl = &type##_vtbl;                                              \
+        object->ref = 1;                                                                           \
+        return object;                                                                             \
+    }
 
 #endif
diff --git a/dlls/windows.storage/streams.c b/dlls/windows.storage/streams.c
index f24990627780..b5d0eefc0339 100644
--- a/dlls/windows.storage/streams.c
+++ b/dlls/windows.storage/streams.c
@@ -1,6 +1,7 @@
 /* WinRT Windows.Storage.Streams Implementation
  *
  * Copyright (C) 2025 Mohamad Al-Jaf
+ * Copyright 2026 Conor McCarthy for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -17,6 +18,7 @@
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  */
 
+#include <assert.h>
 #include "private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(storage);
@@ -164,3 +166,672 @@ static struct random_access_stream_reference_statics random_access_stream_refere
 };
 
 IActivationFactory *random_access_stream_reference_factory = &random_access_stream_reference_statics.IActivationFactory_iface;
+
+/*
+ * InMemoryRandomAccessStream
+ */
+
+struct memory_stream
+{
+    IRandomAccessStream IRandomAccessStream_iface;
+    IInputStream IInputStream_iface;
+    IOutputStream IOutputStream_iface;
+    IClosable IClosable_iface;
+    LONG ref;
+
+    BYTE *buffer;
+    size_t capacity;
+    size_t size;
+    size_t pos;
+    BOOL closed;
+};
+
+static void memory_stream_read( struct memory_stream *impl, IBuffer *buffer, UINT32 count )
+{
+    IBufferByteAccess *access;
+    BYTE *data;
+
+    IBuffer_QueryInterface( buffer, &IID_IBufferByteAccess, (void **)&access );
+    IBufferByteAccess_Buffer( access, &data );
+    IBufferByteAccess_Release( access );
+
+    count = (impl->size >= impl->pos) ? min( count, impl->size - impl->pos ) : 0;
+    memcpy( data, &impl->buffer[impl->pos], count );
+    impl->pos += count;
+    IBuffer_put_Length( buffer, count );
+}
+
+static HRESULT memory_stream_require_capacity( struct memory_stream *impl, size_t capacity )
+{
+    BYTE *new_buffer;
+
+    if (capacity <= impl->capacity)
+        return S_OK;
+
+    capacity = max( capacity, impl->capacity + impl->capacity / 2u );
+    capacity = max( capacity, 0x1000 );
+    new_buffer = realloc( impl->buffer, capacity );
+
+    if (!new_buffer)
+        return HRESULT_FROM_WIN32( ERROR_DISK_FULL );
+
+    /* Zero memory for security and to silence address sanitisers */
+    memset( &new_buffer[impl->capacity], 0, capacity - impl->capacity );
+    impl->capacity = capacity;
+    impl->buffer = new_buffer;
+
+    return S_OK;
+}
+
+static inline struct memory_stream *impl_from_IRandomAccessStream( IRandomAccessStream *iface )
+{
+    return CONTAINING_RECORD( iface, struct memory_stream, IRandomAccessStream_iface );
+}
+
+static HRESULT WINAPI memory_stream_random_access_QueryInterface( IRandomAccessStream *iface, REFIID iid, void **out )
+{
+    struct memory_stream *impl = impl_from_IRandomAccessStream( iface );
+
+    TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out );
+
+    if (IsEqualGUID( iid, &IID_IUnknown )
+            || IsEqualGUID( iid, &IID_IInspectable )
+            || IsEqualGUID( iid, &IID_IRandomAccessStream ))
+    {
+        *out = iface;
+    }
+    else if (IsEqualGUID( iid, &IID_IInputStream ))
+    {
+        *out = &impl->IInputStream_iface;
+    }
+    else if (IsEqualGUID( iid, &IID_IOutputStream ))
+    {
+        *out = &impl->IOutputStream_iface;
+    }
+    else if (IsEqualGUID( iid, &IID_IClosable ))
+    {
+        *out = &impl->IClosable_iface;
+    }
+    else
+    {
+        WARN( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ));
+        *out = NULL;
+        return E_NOINTERFACE;
+    }
+
+    IRandomAccessStream_AddRef( iface );
+    return S_OK;
+}
+
+static ULONG WINAPI memory_stream_random_access_AddRef( IRandomAccessStream *iface )
+{
+    struct memory_stream *impl = impl_from_IRandomAccessStream( iface );
+    ULONG ref = InterlockedIncrement( &impl->ref );
+    TRACE( "iface %p, ref %lu.\n", iface, ref );
+    return ref;
+}
+
+static ULONG WINAPI memory_stream_random_access_Release( IRandomAccessStream *iface )
+{
+    struct memory_stream *impl = impl_from_IRandomAccessStream( iface );
+    ULONG ref = InterlockedDecrement( &impl->ref );
+
+    TRACE( "iface %p, ref %lu.\n", iface, ref );
+
+    if (!ref)
+        free( impl->buffer );
+
+    return ref;
+}
+
+static HRESULT WINAPI memory_stream_random_access_GetIids( IRandomAccessStream *iface, ULONG *iid_count, IID **iids )
+{
+    FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI memory_stream_random_access_GetRuntimeClassName( IRandomAccessStream *iface, HSTRING *class_name )
+{
+    FIXME( "iface %p, class_name %p stub!\n", iface, class_name );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI memory_stream_random_access_GetTrustLevel( IRandomAccessStream *iface, TrustLevel *trust_level )
+{
+    FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI memory_stream_random_access_get_Size( IRandomAccessStream *iface, UINT64 *value )
+{
+    struct memory_stream *impl = impl_from_IRandomAccessStream( iface );
+
+    TRACE( "iface %p, value %p.\n", iface, value );
+
+    if (impl->closed)
+    {
+        *value = 0;
+        return RO_E_CLOSED;
+    }
+
+    *value = impl->size;
+    return S_OK;
+}
+
+static HRESULT WINAPI memory_stream_random_access_put_Size( IRandomAccessStream *iface, UINT64 value )
+{
+    struct memory_stream *impl = impl_from_IRandomAccessStream( iface );
+    HRESULT hr;
+
+    TRACE( "iface %p, value %I64u.\n", iface, value );
+
+    if (impl->closed)
+        return RO_E_CLOSED;
+
+    /* Native truncates the size to 32 bits, which is not replicated here if size_t is 64 bits. */
+    if (FAILED(hr = memory_stream_require_capacity( impl, value )))
+        return hr;
+
+    impl->size = value;
+    return S_OK;
+}
+
+static HRESULT WINAPI memory_stream_random_access_GetInputStreamAt( IRandomAccessStream *iface, UINT64 position,
+        IInputStream **stream )
+{
+    FIXME( "iface %p, position %I64u, stream %p stub!\n", iface, position, stream );
+
+    *stream = NULL;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI memory_stream_random_access_GetOutputStreamAt( IRandomAccessStream *iface, UINT64 position,
+        IOutputStream **stream )
+{
+    FIXME( "iface %p, position %I64u, stream %p stub!\n", iface, position, stream );
+
+    *stream = NULL;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI memory_stream_random_access_get_Position( IRandomAccessStream *iface, UINT64 *value )
+{
+    struct memory_stream *impl = impl_from_IRandomAccessStream( iface );
+
+    TRACE( "iface %p, value %p.\n", iface, value );
+
+    *value = impl->pos;
+    return impl->closed ? RO_E_CLOSED : S_OK;
+}
+
+static HRESULT WINAPI memory_stream_random_access_Seek( IRandomAccessStream *iface, UINT64 position )
+{
+    struct memory_stream *impl = impl_from_IRandomAccessStream( iface );
+
+    TRACE( "iface %p, position %I64u.\n", iface, position );
+
+    if (impl->closed)
+        return RO_E_CLOSED;
+
+    impl->pos = position;
+    return S_OK;
+}
+
+static HRESULT WINAPI memory_stream_random_access_CloneStream( IRandomAccessStream *iface, IRandomAccessStream **stream )
+{
+    FIXME( "iface %p, stream %p stub!\n", iface, stream );
+
+    *stream = NULL;
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI memory_stream_random_access_get_CanRead( IRandomAccessStream *iface, BOOLEAN *value )
+{
+    TRACE( "iface %p, value %p.\n", iface, value );
+
+    *value = TRUE;
+    return S_OK;
+}
+
+static HRESULT WINAPI memory_stream_random_access_get_CanWrite( IRandomAccessStream *iface, BOOLEAN *value )
+{
+    TRACE( "iface %p, value %p.\n", iface, value );
+
+    *value = TRUE;
+    return S_OK;
+}
+
+static const struct IRandomAccessStreamVtbl memory_stream_random_access_vtbl =
+{
+    /* IUnknown methods */
+    memory_stream_random_access_QueryInterface,
+    memory_stream_random_access_AddRef,
+    memory_stream_random_access_Release,
+    /* IInspectable methods */
+    memory_stream_random_access_GetIids,
+    memory_stream_random_access_GetRuntimeClassName,
+    memory_stream_random_access_GetTrustLevel,
+    /* IRandomAccessStream methods */
+    memory_stream_random_access_get_Size,
+    memory_stream_random_access_put_Size,
+    memory_stream_random_access_GetInputStreamAt,
+    memory_stream_random_access_GetOutputStreamAt,
+    memory_stream_random_access_get_Position,
+    memory_stream_random_access_Seek,
+    memory_stream_random_access_CloneStream,
+    memory_stream_random_access_get_CanRead,
+    memory_stream_random_access_get_CanWrite,
+};
+
+DEFINE_IINSPECTABLE( memory_stream_closable, IClosable, struct memory_stream, IRandomAccessStream_iface )
+
+static HRESULT WINAPI memory_stream_closable_Close( IClosable *iface )
+{
+    struct memory_stream *impl = CONTAINING_RECORD( iface, struct memory_stream, IClosable_iface );
+
+    TRACE( "iface %p.\n", iface );
+
+    impl->closed = TRUE;
+    return S_OK;
+}
+
+static const struct IClosableVtbl memory_stream_closable_vtbl =
+{
+    /* IUnknown methods */
+    memory_stream_closable_QueryInterface,
+    memory_stream_closable_AddRef,
+    memory_stream_closable_Release,
+    /* IInspectable methods */
+    memory_stream_closable_GetIids,
+    memory_stream_closable_GetRuntimeClassName,
+    memory_stream_closable_GetTrustLevel,
+    /* IClosable methods */
+    memory_stream_closable_Close,
+};
+
+struct read_async_params
+{
+    IUnknown IUnknown_iface;
+    LONG ref;
+
+    IBuffer *buffer;
+    UINT32 count;
+};
+
+static void read_async_params_destroy( struct read_async_params *params )
+{
+    IBuffer_Release( params->buffer );
+}
+
+DEFINE_ASYNC_PARAMS( read_async_params )
+
+static inline struct memory_stream *impl_from_IInputStream( IInputStream *iface )
+{
+    return CONTAINING_RECORD( iface, struct memory_stream, IInputStream_iface );
+}
+
+static HRESULT WINAPI memory_stream_input_QueryInterface( IInputStream *iface, REFIID iid, void **out )
+{
+    struct memory_stream *impl = impl_from_IInputStream( iface );
+    TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out );
+    return IRandomAccessStream_QueryInterface( &impl->IRandomAccessStream_iface, iid, out );
+}
+
+static ULONG WINAPI memory_stream_input_AddRef( IInputStream *iface )
+{
+    struct memory_stream *impl = impl_from_IInputStream( iface );
+    TRACE( "iface %p.\n", iface );
+    return IRandomAccessStream_AddRef( &impl->IRandomAccessStream_iface );
+}
+
+static ULONG WINAPI memory_stream_input_Release( IInputStream *iface )
+{
+    struct memory_stream *impl = impl_from_IInputStream( iface );
+    TRACE( "iface %p.\n", iface );
+    return IRandomAccessStream_Release( &impl->IRandomAccessStream_iface );
+}
+
+static HRESULT WINAPI memory_stream_input_GetIids( IInputStream *iface, ULONG *iid_count, IID **iids )
+{
+    FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI memory_stream_input_GetRuntimeClassName( IInputStream *iface, HSTRING *class_name )
+{
+    FIXME( "iface %p, class_name %p stub!\n", iface, class_name );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI memory_stream_input_GetTrustLevel( IInputStream *iface, TrustLevel *trust_level )
+{
+    FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level );
+    return E_NOTIMPL;
+}
+
+static HRESULT memory_stream_input_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result, BOOL called_async )
+{
+    struct memory_stream *impl = impl_from_IInputStream( (IInputStream *)invoker );
+    struct read_async_params *params = read_async_params_from_IUnknown( param );
+    UINT32 capacity;
+    HRESULT hr;
+
+    assert( !called_async );
+
+    if (FAILED(hr = IBuffer_get_Capacity( params->buffer, &capacity )))
+        return hr;
+
+    if (capacity < params->count)
+        return E_INVALIDARG;
+
+    memory_stream_read( impl, params->buffer, params->count );
+    result->vt = VT_UNKNOWN;
+    IBuffer_AddRef( params->buffer );
+    result->punkVal = (IUnknown *)params->buffer;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI memory_stream_input_ReadAsync( IInputStream *iface, IBuffer *buffer, UINT32 count,
+        InputStreamOptions options, IAsyncOperationWithProgress_IBuffer_UINT32 **operation )
+{
+    struct memory_stream *impl = impl_from_IInputStream( iface );
+    struct read_async_params *params;
+
+    TRACE( "iface %p, buffer %p, count %u, options %d, operation %p.\n", iface, buffer, count, options, operation );
+
+    *operation = NULL;
+
+    if (!buffer)
+        return E_POINTER;
+
+    if (impl->closed)
+        return RO_E_CLOSED;
+
+    if (!(params = read_async_params_alloc()))
+        return E_OUTOFMEMORY;
+
+    IBuffer_AddRef( buffer );
+    params->buffer = buffer;
+    params->count = count;
+    async_operation_buffer_uint32_create( (IUnknown *)iface, &params->IUnknown_iface, memory_stream_input_async, operation );
+
+    IUnknown_Release( &params->IUnknown_iface );
+
+    return S_OK;
+}
+
+static const struct IInputStreamVtbl memory_stream_input_vtbl =
+{
+    /* IUnknown methods */
+    memory_stream_input_QueryInterface,
+    memory_stream_input_AddRef,
+    memory_stream_input_Release,
+    /* IInspectable methods */
+    memory_stream_input_GetIids,
+    memory_stream_input_GetRuntimeClassName,
+    memory_stream_input_GetTrustLevel,
+    /* IInputStream methods */
+    memory_stream_input_ReadAsync
+};
+
+static inline struct memory_stream *impl_from_IOutputStream( IOutputStream *iface )
+{
+    return CONTAINING_RECORD( iface, struct memory_stream, IOutputStream_iface );
+}
+
+static HRESULT WINAPI memory_stream_output_QueryInterface( IOutputStream *iface, REFIID iid, void **out )
+{
+    struct memory_stream *impl = impl_from_IOutputStream( iface );
+    TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out );
+    return IRandomAccessStream_QueryInterface( &impl->IRandomAccessStream_iface, iid, out );
+}
+
+static ULONG WINAPI memory_stream_output_AddRef( IOutputStream *iface )
+{
+    struct memory_stream *impl = impl_from_IOutputStream( iface );
+    TRACE( "iface %p.\n", iface );
+    return IRandomAccessStream_AddRef( &impl->IRandomAccessStream_iface );
+}
+
+static ULONG WINAPI memory_stream_output_Release( IOutputStream *iface )
+{
+    struct memory_stream *impl = impl_from_IOutputStream( iface );
+    TRACE( "iface %p.\n", iface );
+    return IRandomAccessStream_Release( &impl->IRandomAccessStream_iface );
+}
+
+static HRESULT WINAPI memory_stream_output_GetIids( IOutputStream *iface, ULONG *iid_count, IID **iids )
+{
+    FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI memory_stream_output_GetRuntimeClassName( IOutputStream *iface, HSTRING *class_name )
+{
+    FIXME( "iface %p, class_name %p stub!\n", iface, class_name );
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI memory_stream_output_GetTrustLevel( IOutputStream *iface, TrustLevel *trust_level )
+{
+    FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level );
+    return E_NOTIMPL;
+}
+
+static HRESULT memory_stream_output_async( IUnknown *invoker, IUnknown *param, PROPVARIANT *result, BOOL called_async )
+{
+    struct memory_stream *impl = impl_from_IOutputStream( (IOutputStream *)invoker );
+    IBuffer *buffer = (IBuffer *)param;
+    IBufferByteAccess *access;
+    size_t capacity;
+    UINT32 length;
+    HRESULT hr;
+    BYTE *data;
+
+    assert( !called_async );
+
+    IBuffer_get_Length( buffer, &length );
+
+    if (!length)
+        return S_OK;
+
+    capacity = impl->pos + length;
+    if (capacity < impl->pos || capacity < length)
+        return HRESULT_FROM_WIN32( ERROR_DISK_FULL );
+
+    if (FAILED(hr = memory_stream_require_capacity( impl, capacity )))
+        return hr;
+
+    IBuffer_QueryInterface( buffer, &IID_IBufferByteAccess, (void **)&access );
+    IBufferByteAccess_Buffer( access, &data );
+    IBufferByteAccess_Release( access );
+
+    memcpy( &impl->buffer[impl->pos], data, length );
+    impl->pos += length;
+    impl->size = max( impl->size, impl->pos );
+
+    result->vt = VT_UI4;
+    result->ulVal = length;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI memory_stream_output_WriteAsync( IOutputStream *iface, IBuffer *buffer,
+        IAsyncOperationWithProgress_UINT32_UINT32 **operation )
+{
+    struct memory_stream *impl = impl_from_IOutputStream( iface );
+
+    TRACE( "iface %p, buffer %p, operation %p.\n", iface, buffer, operation );
+
+    *operation = NULL;
+
+    if (!buffer)
+        return E_POINTER;
+
+    if (impl->closed)
+        return RO_E_CLOSED;
+
+    return async_operation_uint32_uint32_create( (IUnknown *)iface, (IUnknown *)buffer, memory_stream_output_async, operation );
+}
+
+static HRESULT WINAPI memory_stream_output_FlushAsync( IOutputStream *iface, IAsyncOperation_boolean **operation )
+{
+    struct memory_stream *impl = impl_from_IOutputStream( iface );
+
+    FIXME( "iface %p, operation %p stub!\n", iface, operation );
+
+    *operation = NULL;
+
+    if (impl->closed)
+        return RO_E_CLOSED;
+
+    return E_NOTIMPL;
+}
+
+static const struct IOutputStreamVtbl memory_stream_output_vtbl =
+{
+    /* IUnknown methods */
+    memory_stream_output_QueryInterface,
+    memory_stream_output_AddRef,
+    memory_stream_output_Release,
+    /* IInspectable methods */
+    memory_stream_output_GetIids,
+    memory_stream_output_GetRuntimeClassName,
+    memory_stream_output_GetTrustLevel,
+    /* IOutputStream methods */
+    memory_stream_output_WriteAsync,
+    memory_stream_output_FlushAsync,
+};
+
+static HRESULT memory_stream_create( IRandomAccessStream **out )
+{
+    struct memory_stream *impl;
+
+    TRACE( "out %p.\n", out );
+
+    if (!(impl = calloc( 1, sizeof(*impl) )))
+    {
+        *out = NULL;
+        return E_OUTOFMEMORY;
+    }
+
+    impl->ref = 1;
+    impl->IRandomAccessStream_iface.lpVtbl = &memory_stream_random_access_vtbl;
+    impl->IInputStream_iface.lpVtbl = &memory_stream_input_vtbl;
+    impl->IOutputStream_iface.lpVtbl = &memory_stream_output_vtbl;
+    impl->IClosable_iface.lpVtbl = &memory_stream_closable_vtbl;
+
+    *out = &impl->IRandomAccessStream_iface;
+    return S_OK;
+}
+
+struct memory_stream_factory
+{
+    IActivationFactory IActivationFactory_iface;
+    LONG ref;
+};
+
+static inline struct memory_stream_factory *impl_memory_stream_factory_from_IActivationFactory( IActivationFactory *iface )
+{
+    return CONTAINING_RECORD( iface, struct memory_stream_factory, IActivationFactory_iface );
+}
+
+static HRESULT STDMETHODCALLTYPE memory_stream_activation_factory_QueryInterface( IActivationFactory *iface, REFIID iid,
+        void **out )
+{
+    TRACE( "iface %p, iid %s, out %p.\n", iface, debugstr_guid( iid ), out );
+
+    if (IsEqualGUID( iid, &IID_IUnknown )
+            || IsEqualGUID( iid, &IID_IInspectable )
+            || IsEqualGUID( iid, &IID_IAgileObject )
+            || IsEqualGUID( iid, &IID_IActivationFactory ))
+    {
+        IUnknown_AddRef( iface );
+        *out = iface;
+        return S_OK;
+    }
+
+    WARN( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ));
+    *out = NULL;
+    return E_NOINTERFACE;
+}
+
+static ULONG STDMETHODCALLTYPE memory_stream_activation_factory_AddRef( IActivationFactory *iface )
+{
+    struct memory_stream_factory *impl = impl_memory_stream_factory_from_IActivationFactory( iface );
+    ULONG ref = InterlockedIncrement( &impl->ref );
+    TRACE( "iface %p, ref %lu.\n", iface, ref );
+    return ref;
+}
+
+static ULONG STDMETHODCALLTYPE memory_stream_activation_factory_Release( IActivationFactory *iface )
+{
+    struct memory_stream_factory *impl = impl_memory_stream_factory_from_IActivationFactory( iface );
+    ULONG ref = InterlockedDecrement( &impl->ref );
+    TRACE( "iface %p, ref %lu.\n", iface, ref );
+    return ref;
+}
+
+static HRESULT STDMETHODCALLTYPE memory_stream_activation_factory_GetIids( IActivationFactory *iface, ULONG *iid_count,
+        IID **iids )
+{
+    FIXME( "iface %p, iid_count %p, iids %p stub!\n", iface, iid_count, iids );
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE memory_stream_activation_factory_GetRuntimeClassName( IActivationFactory *iface,
+        HSTRING *class_name )
+{
+    FIXME( "iface %p, class_name %p stub!\n", iface, class_name );
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE memory_stream_activation_factory_GetTrustLevel( IActivationFactory *iface,
+        TrustLevel *trust_level )
+{
+    FIXME( "iface %p, trust_level %p stub!\n", iface, trust_level );
+    return E_NOTIMPL;
+}
+
+static HRESULT STDMETHODCALLTYPE memory_stream_activation_factory_ActivateInstance( IActivationFactory *iface,
+        IInspectable **instance )
+{
+    IRandomAccessStream *out;
+    HRESULT hr;
+
+    TRACE( "iface %p, instance %p.\n", iface, instance );
+
+    *instance = NULL;
+
+    if (SUCCEEDED(hr = memory_stream_create( &out )))
+    {
+        hr = IRandomAccessStream_QueryInterface( out, &IID_IInspectable, (void **)instance );
+        IRandomAccessStream_Release( out );
+        if (SUCCEEDED(hr))
+            TRACE( "created InMemoryRandomAccessStream %p.\n", *instance );
+    }
+
+    return hr;
+}
+
+static const struct IActivationFactoryVtbl memory_stream_activation_factory_vtbl =
+{
+    memory_stream_activation_factory_QueryInterface,
+    memory_stream_activation_factory_AddRef,
+    memory_stream_activation_factory_Release,
+    /* IInspectable methods */
+    memory_stream_activation_factory_GetIids,
+    memory_stream_activation_factory_GetRuntimeClassName,
+    memory_stream_activation_factory_GetTrustLevel,
+    /* IActivationFactory methods */
+    memory_stream_activation_factory_ActivateInstance,
+};
+
+struct memory_stream_factory memory_stream_factory =
+{
+    {&memory_stream_activation_factory_vtbl},
+    1
+};
+
+IActivationFactory *memory_stream_activation_factory = &memory_stream_factory.IActivationFactory_iface;
diff --git a/dlls/windows.storage/tests/storage.c b/dlls/windows.storage/tests/storage.c
index 85bda00e044c..e6a721f43ac5 100644
--- a/dlls/windows.storage/tests/storage.c
+++ b/dlls/windows.storage/tests/storage.c
@@ -1,5 +1,6 @@
 /*
  * Copyright (C) 2025 Mohamad Al-Jaf
+ * Copyright 2026 Conor McCarthy for CodeWeavers
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -30,6 +31,7 @@
 #include "windows.foundation.h"
 #define WIDL_using_Windows_Storage_Streams
 #include "windows.storage.streams.h"
+#include "robuffer.h"
 
 #include "wine/test.h"
 
@@ -46,6 +48,113 @@ static void check_interface_( unsigned int line, void *obj, const IID *iid, BOOL
         IUnknown_Release( unk );
 }
 
+#define DEFINE_ASYNC_COMPLETED_HANDLER( name, iface_type, async_type )                              \
+    struct name                                                                                     \
+    {                                                                                               \
+        iface_type iface_type##_iface;                                                              \
+        LONG ref;                                                                                   \
+        BOOL invoked;                                                                               \
+        HANDLE event;                                                                               \
+    };                                                                                              \
+                                                                                                    \
+    static HRESULT WINAPI name##_QueryInterface( iface_type *iface, REFIID iid, void **out )        \
+    {                                                                                               \
+        if (IsEqualGUID( iid, &IID_IUnknown ) || IsEqualGUID( iid, &IID_IAgileObject ) ||           \
+            IsEqualGUID( iid, &IID_##iface_type ))                                                  \
+        {                                                                                           \
+            IUnknown_AddRef( iface );                                                               \
+            *out = iface;                                                                           \
+            return S_OK;                                                                            \
+        }                                                                                           \
+                                                                                                    \
+        trace( "%s not implemented, returning E_NOINTERFACE.\n", debugstr_guid( iid ) );            \
+        *out = NULL;                                                                                \
+        return E_NOINTERFACE;                                                                       \
+    }                                                                                               \
+                                                                                                    \
+    static ULONG WINAPI name##_AddRef( iface_type *iface )                                          \
+    {                                                                                               \
+        struct name *impl = CONTAINING_RECORD( iface, struct name, iface_type##_iface );            \
+        return InterlockedIncrement( &impl->ref );                                                  \
+    }                                                                                               \
+                                                                                                    \
+    static ULONG WINAPI name##_Release( iface_type *iface )                                         \
+    {                                                                                               \
+        struct name *impl = CONTAINING_RECORD( iface, struct name, iface_type##_iface );            \
+        ULONG ref = InterlockedDecrement( &impl->ref );                                             \
+        if (!ref) free( impl );                                                                     \
+        return ref;                                                                                 \
+    }                                                                                               \
+                                                                                                    \
+    static HRESULT WINAPI name##_Invoke( iface_type *iface, async_type *async, AsyncStatus status ) \
+    {                                                                                               \
+        struct name *impl = CONTAINING_RECORD( iface, struct name, iface_type##_iface );            \
+        ok( !impl->invoked, "invoked twice\n" );                                                    \
+        impl->invoked = TRUE;                                                                       \
+        if (impl->event) SetEvent( impl->event );                                                   \
+        return S_OK;                                                                                \
+    }                                                                                               \
+                                                                                                    \
+    static iface_type##Vtbl name##_vtbl =                                                           \
+    {                                                                                               \
+        name##_QueryInterface,                                                                      \
+        name##_AddRef,                                                                              \
+        name##_Release,                                                                             \
+        name##_Invoke,                                                                              \
+    };                                                                                              \
+                                                                                                    \
+    static iface_type *name##_create( HANDLE event )                                                \
+    {                                                                                               \
+        struct name *impl;                                                                          \
+                                                                                                    \
+        if (!(impl = calloc( 1, sizeof(*impl) ))) return NULL;                                      \
+        impl->iface_type##_iface.lpVtbl = &name##_vtbl;                                             \
+        impl->event = event;                                                                        \
+        impl->ref = 1;                                                                              \
+                                                                                                    \
+        return &impl->iface_type##_iface;                                                           \
+    }                                                                                               \
+                                                                                                    \
+    static DWORD await_##async_type( async_type *async, DWORD timeout )                             \
+    {                                                                                               \
+        iface_type *handler;                                                                        \
+        HANDLE event;                                                                               \
+        HRESULT hr;                                                                                 \
+        DWORD ret;                                                                                  \
+                                                                                                    \
+        event = CreateEventW( NULL, FALSE, FALSE, NULL );                                           \
+        ok( !!event, "CreateEventW failed, error %lu\n", GetLastError() );                          \
+        handler = name##_create( event );                                                           \
+        ok( !!handler, "Failed to create completion handler\n" );                                   \
+        hr = async_type##_put_Completed( async, handler );                                          \
+        ok( hr == S_OK, "put_Completed returned %#lx\n", hr );                                      \
+        ret = WaitForSingleObject( event, timeout );                                                \
+        ok( !ret, "WaitForSingleObject returned %#lx\n", ret );                                     \
+        CloseHandle( event );                                                                       \
+        iface_type##_Release( handler );                                                            \
+                                                                                                    \
+        return ret;                                                                                 \
+    }
+
+static HRESULT get_activation_factory( const WCHAR *name, IActivationFactory **factory )
+{
+    HSTRING str = NULL;
+    HRESULT hr;
+
+    hr = WindowsCreateString( name, wcslen( name ), &str );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    hr = RoGetActivationFactory( str, &IID_IActivationFactory, (void **)factory );
+    WindowsDeleteString( str );
+    ok( hr == S_OK || broken( hr == REGDB_E_CLASSNOTREG ), "got hr %#lx.\n", hr );
+
+    if (hr == REGDB_E_CLASSNOTREG)
+        win_skip( "%s runtimeclass not registered, skipping tests.\n", wine_dbgstr_w( name ) );
+    else if (hr == CLASS_E_CLASSNOTAVAILABLE)
+        skip( "%s runtimeclass not available, skipping tests.\n", wine_dbgstr_w( name ) );
+
+    return hr;
+}
+
 static void test_RandomAccessStreamReference(void)
 {
     static const WCHAR *random_access_stream_reference_statics_name = L"Windows.Storage.Streams.RandomAccessStreamReference";
@@ -84,6 +193,339 @@ static void test_RandomAccessStreamReference(void)
     ok( ref == 0, "got ref %ld.\n", ref );
 }
 
+DEFINE_ASYNC_COMPLETED_HANDLER( async_uint32_uint32_completed_handler, \
+        IAsyncOperationWithProgressCompletedHandler_UINT32_UINT32, IAsyncOperationWithProgress_UINT32_UINT32 )
+
+DEFINE_ASYNC_COMPLETED_HANDLER( async_buffer_uint32_completed_handler, \
+        IAsyncOperationWithProgressCompletedHandler_IBuffer_UINT32, IAsyncOperationWithProgress_IBuffer_UINT32 )
+
+#define check_async_info( a, b, c ) check_async_info_( __LINE__, a, b, c )
+static void check_async_info_( int line, void *async, AsyncStatus expect_status, HRESULT expect_hr )
+{
+    AsyncStatus async_status;
+    IAsyncInfo *async_info;
+    HRESULT hr, async_hr;
+    UINT32 async_id;
+
+    hr = IInspectable_QueryInterface( async, &IID_IAsyncInfo, (void **)&async_info );
+    ok_(__FILE__, line)( hr == S_OK, "QueryInterface returned %#lx\n", hr );
+
+    async_id = 0xdeadbeef;
+    hr = IAsyncInfo_get_Id( async_info, &async_id );
+    if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_Id returned %#lx\n", hr );
+    else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_Id returned %#lx\n", hr );
+    ok_(__FILE__, line)( !!async_id, "got id %u\n", async_id );
+
+    async_status = 0xdeadbeef;
+    hr = IAsyncInfo_get_Status( async_info, &async_status );
+    if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_Status returned %#lx\n", hr );
+    else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_Status returned %#lx\n", hr );
+    ok_(__FILE__, line)( async_status == expect_status, "got status %u\n", async_status );
+
+    async_hr = 0xdeadbeef;
+    hr = IAsyncInfo_get_ErrorCode( async_info, &async_hr );
+    if (expect_status < 4) ok_(__FILE__, line)( hr == S_OK, "get_ErrorCode returned %#lx\n", hr );
+    else ok_(__FILE__, line)( hr == E_ILLEGAL_METHOD_CALL, "get_ErrorCode returned %#lx\n", hr );
+    if (expect_status < 4) ok_(__FILE__, line)( async_hr == expect_hr, "got error %#lx\n", async_hr );
+    else ok_(__FILE__, line)( async_hr == E_ILLEGAL_METHOD_CALL, "got error %#lx\n", async_hr );
+
+    IAsyncInfo_Release( async_info );
+}
+
+#define output_stream_write( a, b, c ) output_stream_write_(__LINE__, a, b, c )
+static void output_stream_write_( unsigned int line, IOutputStream *output_stream, IBuffer *buffer, UINT32 count )
+{
+    IAsyncOperationWithProgress_UINT32_UINT32 *operation;
+    UINT32 written;
+    HRESULT hr;
+    UINT res;
+
+    hr = IBuffer_put_Length( buffer, count );
+    ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr );
+    hr = IOutputStream_WriteAsync( output_stream, buffer, &operation );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    res = await_IAsyncOperationWithProgress_UINT32_UINT32( operation, 1000 );
+    ok( res == 0, "await_IAsyncOperationWithProgress_UINT32_UINT32 returned %#x\n", res );
+    check_async_info( operation, Completed, S_OK );
+    hr = IAsyncOperationWithProgress_UINT32_UINT32_GetResults( operation, &written );
+    ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr );
+    ok_(__FILE__, line)( written == count, "wrote %u bytes.\n", written );
+    IAsyncOperationWithProgress_UINT32_UINT32_Release( operation );
+}
+
+#define input_stream_read( a, b, c, d, e, f ) input_stream_read_(__LINE__, a, b, c, d, e, f )
+static void input_stream_read_( unsigned int line, IInputStream *input_stream, IBuffer *buffer, UINT32 count,
+        AsyncStatus expect_status, HRESULT expect_hr, IBuffer **res_buffer )
+{
+    IAsyncOperationWithProgress_IBuffer_UINT32 *operation;
+    HRESULT hr;
+    UINT res;
+
+    hr = IInputStream_ReadAsync( input_stream, buffer, count, 0, &operation );
+    ok_(__FILE__, line)( hr == S_OK, "got hr %#lx.\n", hr );
+    res = await_IAsyncOperationWithProgress_IBuffer_UINT32( operation, 1000 );
+    ok_(__FILE__, line)( res == 0, "await_IAsyncOperationWithProgress_IBuffer_UINT32 returned %#x\n", res );
+    check_async_info( operation, expect_status, expect_hr );
+    hr = IAsyncOperationWithProgress_IBuffer_UINT32_GetResults( operation, res_buffer );
+    ok_(__FILE__, line)( hr == expect_hr, "got hr %#lx.\n", hr );
+    IAsyncOperationWithProgress_IBuffer_UINT32_Release( operation );
+}
+
+static BYTE *buffer_get_data( IBuffer *buffer )
+{
+    IBufferByteAccess *access;
+    BYTE *data = NULL;
+    HRESULT hr;
+
+    hr = IBuffer_QueryInterface( buffer, &IID_IBufferByteAccess, (void **)&access );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    hr = IBufferByteAccess_Buffer( access, &data );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    IBufferByteAccess_Release( access );
+
+    return data;
+}
+
+static void test_InMemoryRandomAccessStream(void)
+{
+    static const WCHAR *in_memory_stream_statics_name = L"Windows.Storage.Streams.InMemoryRandomAccessStream";
+    static const WCHAR *buffer_statics_name = L"Windows.Storage.Streams.Buffer";
+    IRandomAccessStream *in_memory_stream = (void *)0xdeadbeef;
+    IInspectable *in_memory_inspectable = (void *)0xdeadbeef;
+    IAsyncOperationWithProgress_IBuffer_UINT32 *operation;
+    IAsyncOperationWithProgress_UINT32_UINT32 *write_op;
+    IActivationFactory *factory = (void *)0xdeadbeef;
+    const UINT64 uint64_value = 0xdeadbeefcafef00d;
+    IBuffer *buffer, *res_buffer = NULL;
+    IBufferFactory *buffer_factory;
+    IOutputStream *output_stream;
+    IInputStream *input_stream;
+    BYTE byte_value = 0xab;
+    BOOLEAN value_bool = 0;
+    IClosable *closable;
+    UINT64 value64 = 0;
+    BYTE *data = NULL;
+    UINT32 value = 0;
+    HRESULT hr;
+    LONG ref;
+
+    if (FAILED(hr = get_activation_factory( in_memory_stream_statics_name, &factory )))
+        return;
+
+    check_interface( factory, &IID_IUnknown, FALSE );
+    check_interface( factory, &IID_IInspectable, FALSE );
+    check_interface( factory, &IID_IAgileObject, TRUE /* Supported after Windows 10 1607 */ );
+
+    hr = IActivationFactory_ActivateInstance( factory, &in_memory_inspectable );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ref = IActivationFactory_Release( factory );
+    flaky ok( ref == 1, "got ref %ld.\n", ref );
+    hr = IInspectable_QueryInterface( in_memory_inspectable, &IID_IRandomAccessStream, (void **)&in_memory_stream );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    IInspectable_Release( in_memory_inspectable );
+
+    check_interface( in_memory_stream, &IID_IClosable, FALSE );
+
+    hr = IRandomAccessStream_QueryInterface( in_memory_stream, &IID_IInputStream, (void **)&input_stream );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    check_interface( input_stream, &IID_IUnknown, FALSE );
+    check_interface( input_stream, &IID_IInspectable, FALSE );
+    check_interface( input_stream, &IID_IRandomAccessStream, FALSE );
+    check_interface( input_stream, &IID_IOutputStream, FALSE );
+    check_interface( input_stream, &IID_IClosable, FALSE );
+
+    hr = IRandomAccessStream_QueryInterface( in_memory_stream, &IID_IOutputStream, (void **)&output_stream );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    check_interface( output_stream, &IID_IUnknown, FALSE );
+    check_interface( output_stream, &IID_IInspectable, FALSE );
+    check_interface( output_stream, &IID_IRandomAccessStream, FALSE );
+    check_interface( output_stream, &IID_IInputStream, FALSE );
+
+    if (FAILED(hr = get_activation_factory( buffer_statics_name, &factory )))
+        return;
+
+    hr = IActivationFactory_QueryInterface( factory, &IID_IBufferFactory, (void **)&buffer_factory );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    hr = IBufferFactory_Create( buffer_factory, 17, &buffer );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    IBufferFactory_Release( buffer_factory );
+
+    IActivationFactory_Release( factory );
+
+    data = buffer_get_data( buffer );
+
+    hr = IRandomAccessStream_get_Size( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 0, "got size %I64u.\n", value64 );
+    hr = IRandomAccessStream_get_Position( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 0, "got pos %I64u.\n", value64 );
+    hr = IRandomAccessStream_Seek( in_memory_stream, 16 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    hr = IRandomAccessStream_get_Size( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 0, "got size %I64u.\n", value64 );
+    hr = IRandomAccessStream_get_Position( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 16, "got pos %I64u.\n", value64 );
+
+    /* Write 0 bytes at position 16 */
+    output_stream_write( output_stream, buffer, 0 );
+    hr = IRandomAccessStream_get_Size( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 0, "got size %I64u.\n", value64 );
+    /* Write 1 byte at position 16 */
+    data[0] = byte_value;
+    output_stream_write( output_stream, buffer, 1 );
+    hr = IRandomAccessStream_get_Size( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 17, "got size %I64u.\n", value64 );
+
+    hr = IRandomAccessStream_Seek( in_memory_stream, 0 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    hr = IRandomAccessStream_get_Position( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 0, "got pos %I64u.\n", value64 );
+    memcpy( data, &uint64_value, 8 );
+    output_stream_write( output_stream, buffer, 8 );
+    hr = IRandomAccessStream_get_Size( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 17, "got size %I64u.\n", value64 );
+    hr = IRandomAccessStream_get_Position( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 8, "got pos %I64u.\n", value64 );
+
+    hr = IOutputStream_WriteAsync( output_stream, NULL, &write_op );
+    ok( hr == E_POINTER, "got hr %#lx.\n", hr );
+    /* Crashes on Windows if the op pointer is null
+     * hr = IOutputStream_WriteAsync( output_stream, NULL, NULL ); */
+
+    memset( data, 0xcd, 17 );
+
+    hr = IRandomAccessStream_Seek( in_memory_stream, 0 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    res_buffer = NULL;
+    input_stream_read( input_stream, buffer, 0, Completed, S_OK, &res_buffer );
+    ok( res_buffer == buffer, "got different buffer.\n" );
+    hr = IBuffer_get_Length( res_buffer, &value );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value == 0, "got read %u.\n", value );
+    IBuffer_Release( res_buffer );
+
+    res_buffer = (void *)0xdeadbeef;
+    input_stream_read( input_stream, buffer, 20, Error, E_INVALIDARG, &res_buffer );
+    ok( !res_buffer, "got res_buffer %p.\n", res_buffer );
+
+    res_buffer = NULL;
+    input_stream_read( input_stream, buffer, 17, Completed, S_OK, &res_buffer );
+    ok( res_buffer == buffer, "got different buffer.\n" );
+    hr = IBuffer_get_Length( res_buffer, &value );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value == 17, "got read %u.\n", value );
+    hr = IRandomAccessStream_get_Position( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 17, "got pos %I64u.\n", value64 );
+    data = buffer_get_data( res_buffer );
+    memcpy( &value64, data, sizeof(value64 ) );
+    ok( value64 == uint64_value, "got value64 %#I64x.\n", value64 );
+    ok ( data[16] == byte_value, "got byte value %#x.\n", data[16]);
+    IBuffer_Release( res_buffer );
+
+    hr = IRandomAccessStream_Seek( in_memory_stream, 18 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    res_buffer = NULL;
+    input_stream_read( input_stream, buffer, 1, Completed, S_OK, &res_buffer );
+    ok( res_buffer == buffer, "got different buffer.\n" );
+    hr = IBuffer_get_Length( res_buffer, &value );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value == 0, "got read %u.\n", value );
+    IBuffer_Release( res_buffer );
+
+    operation = (void *)0xdeadbeef;
+    hr = IInputStream_ReadAsync( input_stream, NULL, 1, 0, &operation );
+    ok( hr == E_POINTER, "got hr %#lx.\n", hr );
+    ok( !operation, "got operation %p.\n", operation );
+    /* Crashes on Windows if the op pointer is null
+     * hr = IInputStream_ReadAsync( input_stream, NULL, 1, 0, NULL ); */
+
+    value_bool = 0;
+    hr = IRandomAccessStream_get_CanRead( in_memory_stream, &value_bool );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value_bool == TRUE, "got bool %#x.\n", value_bool );
+    value_bool = 0;
+    hr = IRandomAccessStream_get_CanWrite( in_memory_stream, &value_bool );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value_bool == TRUE, "got bool %#x.\n", value_bool );
+
+    /* Test large put size and read. Native truncates put_Size() to 32-bit,
+     * but only allows reading from positions less than 1 << 31
+     * Both of these seem potentially subject to change, so are untested. */
+    hr = IRandomAccessStream_put_Size( in_memory_stream, 0x100000 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    hr = IRandomAccessStream_get_Size( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 0x100000, "got size %I64u.\n", value64 );
+    hr = IRandomAccessStream_get_Position( in_memory_stream, &value64 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value64 == 18, "got pos %I64u.\n", value64 );
+
+    hr = IRandomAccessStream_Seek( in_memory_stream, 0xffff8 );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    res_buffer = NULL;
+    input_stream_read( input_stream, buffer, 8, Completed, S_OK, &res_buffer );
+    ok( res_buffer == buffer, "got different buffer.\n" );
+    hr = IBuffer_get_Length( res_buffer, &value );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value == 8, "got read %u.\n", value );
+    data = buffer_get_data( res_buffer );
+    memcpy( &value64, data, sizeof(value64) );
+    ok( value64 == 0, "got value64 %#I64x.\n", value64 );
+    IBuffer_Release( res_buffer );
+
+    hr = IOutputStream_QueryInterface( output_stream, &IID_IClosable, (void **)&closable );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    hr = IClosable_Close( closable );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    IClosable_Release( closable );
+
+    write_op = (void *)0xdeadbeef;
+    hr = IOutputStream_WriteAsync( output_stream, buffer, &write_op );
+    ok( hr == RO_E_CLOSED, "got hr %#lx.\n", hr );
+    ok( !write_op, "got operation %p.\n", write_op );
+    operation = (void *)0xdeadbeef;
+    hr = IInputStream_ReadAsync( input_stream, buffer, 0, 0, &operation );
+    ok( hr == RO_E_CLOSED, "got hr %#lx.\n", hr );
+    ok( !operation, "got operation %p.\n", operation );
+
+    hr = IRandomAccessStream_Seek( in_memory_stream, 0 );
+    ok( hr == RO_E_CLOSED, "got hr %#lx.\n", hr );
+    value64 = uint64_value;
+    hr = IRandomAccessStream_get_Size( in_memory_stream, &value64 );
+    ok( hr == RO_E_CLOSED, "got hr %#lx.\n", hr );
+    ok( value64 == 0, "got size %I64u.\n", value64 );
+    hr = IRandomAccessStream_put_Size( in_memory_stream, 1 );
+    ok( hr == RO_E_CLOSED, "got hr %#lx.\n", hr );
+    value64 = uint64_value;
+    hr = IRandomAccessStream_get_Position( in_memory_stream, &value64 );
+    ok( hr == RO_E_CLOSED, "got hr %#lx.\n", hr );
+    ok( value64 == 0x100000, "got pos %I64u.\n", value64 );
+    value_bool = 0;
+    hr = IRandomAccessStream_get_CanRead( in_memory_stream, &value_bool );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value_bool == TRUE, "got bool %#x.\n", value_bool );
+    value_bool = 0;
+    hr = IRandomAccessStream_get_CanWrite( in_memory_stream, &value_bool );
+    ok( hr == S_OK, "got hr %#lx.\n", hr );
+    ok( value_bool == TRUE, "got bool %#x.\n", value_bool );
+
+    IBuffer_Release( buffer );
+    IOutputStream_Release( output_stream );
+    IInputStream_Release( input_stream );
+
+    ref = IRandomAccessStream_Release( in_memory_stream );
+    ok( ref == 0, "got ref %ld.\n", ref );
+}
+
 START_TEST(storage)
 {
     HRESULT hr;
@@ -92,6 +534,7 @@ START_TEST(storage)
     ok( hr == S_OK, "RoInitialize failed, hr %#lx\n", hr );
 
     test_RandomAccessStreamReference();
+    test_InMemoryRandomAccessStream();
 
     RoUninitialize();
 }
diff --git a/dlls/wineandroid.drv/android.h b/dlls/wineandroid.drv/android.h
index 1d3df21d10ff..3e69e52c7366 100644
--- a/dlls/wineandroid.drv/android.h
+++ b/dlls/wineandroid.drv/android.h
@@ -132,7 +132,6 @@ enum android_window_messages
     WM_ANDROID_REFRESH = WM_WINE_FIRST_DRIVER_MSG,
 };
 
-extern HWND get_capture_window(void);
 extern void init_monitors( int width, int height );
 extern void set_screen_dpi( DWORD dpi );
 extern void update_keyboard_lock_state( WORD vkey, UINT state );
diff --git a/dlls/wineandroid.drv/device.c b/dlls/wineandroid.drv/device.c
index c660c656776a..c1071eff4a6b 100644
--- a/dlls/wineandroid.drv/device.c
+++ b/dlls/wineandroid.drv/device.c
@@ -79,7 +79,6 @@ static jobject java_object;
 #define SYNC_IOC_WAIT _IOW('>', 0, __s32)
 #endif
 
-static HWND capture_window;
 static HWND desktop_window;
 
 int event_sink = -1;
@@ -401,7 +400,6 @@ static void free_native_win_data( struct native_win_data *data )
 {
     unsigned int idx = data_map_idx( data->hwnd, data->opengl );
 
-    InterlockedCompareExchangePointer( (void **)&capture_window, 0, data->hwnd );
     release_native_window( data );
     free( data );
     data_map[idx] = NULL;
@@ -455,12 +453,6 @@ end:
     pthread_mutex_unlock(&dispatch_ioctl_lock);
 }
 
-/* get the capture window stored in the desktop process */
-HWND get_capture_window(void)
-{
-    return capture_window;
-}
-
 static jobject load_java_method( JNIEnv* env, jmethodID *method, const char *name, const char *args )
 {
     if (!*method)
@@ -809,7 +801,6 @@ static int setCapture_ioctl( JNIEnv* env, void *data, DWORD in_size, DWORD out_s
 
     LOG( TRACE, "hwnd %08x\n", res->hdr.hwnd );
 
-    InterlockedExchangePointer( (void **)&capture_window, LongToHandle( res->hdr.hwnd ));
     return 0;
 }
 
diff --git a/dlls/wineandroid.drv/window.c b/dlls/wineandroid.drv/window.c
index 3cc971558ff6..bf00d73c2351 100644
--- a/dlls/wineandroid.drv/window.c
+++ b/dlls/wineandroid.drv/window.c
@@ -296,7 +296,7 @@ jboolean motion_event( JNIEnv *env, jobject obj, jint win, jint action, jint x,
     data.motion.input.mi.mouseData   = 0;
     data.motion.input.mi.time        = 0;
     data.motion.input.mi.dwExtraInfo = 0;
-    data.motion.input.mi.dwFlags     = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
+    data.motion.input.mi.dwFlags     = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE_NOCOALESCE;
     switch (action & AMOTION_EVENT_ACTION_MASK)
     {
     case AMOTION_EVENT_ACTION_DOWN:
@@ -459,8 +459,6 @@ static int process_events( DWORD mask )
 
         case MOTION_EVENT:
             {
-                HWND capture = get_capture_window();
-
                 if (event->data.motion.input.mi.dwFlags & (MOUSEEVENTF_LEFTDOWN|MOUSEEVENTF_RIGHTDOWN|MOUSEEVENTF_MIDDLEDOWN))
                     TRACE( "BUTTONDOWN pos %d,%d hwnd %p flags %x\n",
                            event->data.motion.input.mi.dx, event->data.motion.input.mi.dy,
@@ -473,21 +471,8 @@ static int process_events( DWORD mask )
                     TRACE( "MOUSEMOVE pos %d,%d hwnd %p flags %x\n",
                            event->data.motion.input.mi.dx, event->data.motion.input.mi.dy,
                            event->data.motion.hwnd, event->data.motion.input.mi.dwFlags );
-                if (!capture && (event->data.motion.input.mi.dwFlags & MOUSEEVENTF_ABSOLUTE))
-                {
-                    RECT rect;
-                    SetRect( &rect, event->data.motion.input.mi.dx, event->data.motion.input.mi.dy,
-                             event->data.motion.input.mi.dx + 1, event->data.motion.input.mi.dy + 1 );
-
-                    SERVER_START_REQ( update_window_zorder )
-                    {
-                        req->window      = wine_server_user_handle( event->data.motion.hwnd );
-                        req->rect        = wine_server_rectangle( rect );
-                        wine_server_call( req );
-                    }
-                    SERVER_END_REQ;
-                }
-                NtUserSendHardwareInput( capture ? capture : event->data.motion.hwnd, 0, &event->data.motion.input, 0 );
+
+                NtUserSendHardwareInput( event->data.motion.hwnd, 0, &event->data.motion.input, 0 );
             }
             break;
 
@@ -1196,7 +1181,7 @@ LRESULT ANDROID_WindowMessage( HWND hwnd, UINT msg, WPARAM wp, LPARAM lp )
         }
         else
         {
-            NtUserExposeWindowSurface( hwnd, 0, NULL, 0 );
+            NtUserExposeWindowSurface( hwnd, 0, NULL );
         }
         return 0;
     default:
diff --git a/dlls/wined3d/cs.c b/dlls/wined3d/cs.c
index 7b17b1d35573..41869e2922c5 100644
--- a/dlls/wined3d/cs.c
+++ b/dlls/wined3d/cs.c
@@ -755,9 +755,7 @@ static void wined3d_cs_exec_present(struct wined3d_cs *cs, const void *data)
         }
     }
 
-    InterlockedDecrement(&cs->pending_presents);
-    if (InterlockedCompareExchange(&cs->waiting_for_present, FALSE, TRUE))
-        SetEvent(cs->present_event);
+    ReleaseSemaphore(swapchain->frame_latency_semaphore, 1, NULL);
 }
 
 void wined3d_cs_emit_present(struct wined3d_cs *cs, struct wined3d_swapchain *swapchain,
@@ -766,7 +764,6 @@ void wined3d_cs_emit_present(struct wined3d_cs *cs, struct wined3d_swapchain *sw
 {
     struct wined3d_cs_present *op;
     unsigned int i;
-    LONG pending;
 
     wined3d_not_from_cs(cs);
 
@@ -779,8 +776,6 @@ void wined3d_cs_emit_present(struct wined3d_cs *cs, struct wined3d_swapchain *sw
     op->swap_interval = swap_interval;
     op->flags = flags;
 
-    pending = InterlockedIncrement(&cs->pending_presents);
-
     wined3d_resource_reference(&swapchain->front_buffer->resource);
     for (i = 0; i < swapchain->state.desc.backbuffer_count; ++i)
     {
@@ -789,21 +784,11 @@ void wined3d_cs_emit_present(struct wined3d_cs *cs, struct wined3d_swapchain *sw
 
     wined3d_device_context_submit(&cs->c, WINED3D_CS_QUEUE_DEFAULT);
 
-    /* Limit input latency by limiting the number of presents that we can get
-     * ahead of the worker thread. */
-    while (pending >= swapchain->max_frame_latency)
+    if (!(swapchain->state.desc.flags & WINED3D_SWAPCHAIN_FRAME_LATENCY_WAITABLE_OBJECT))
     {
-        InterlockedExchange(&cs->waiting_for_present, TRUE);
-
-        pending = InterlockedCompareExchange(&cs->pending_presents, 0, 0);
-        if (pending >= swapchain->max_frame_latency || !InterlockedCompareExchange(&cs->waiting_for_present, FALSE, TRUE))
-        {
-            TRACE_(d3d_perf)("Reached latency limit (%u frames), blocking to wait.\n", swapchain->max_frame_latency);
-            wined3d_mutex_unlock();
-            WaitForSingleObject(cs->present_event, INFINITE);
-            wined3d_mutex_lock();
-            TRACE_(d3d_perf)("Woken up from the wait.\n");
-        }
+        /* Limit input latency by limiting the number of presents that we can
+         * get ahead of the worker thread. */
+        WaitForSingleObject(swapchain->frame_latency_semaphore, INFINITE);
     }
 }
 
@@ -2799,13 +2784,12 @@ void wined3d_device_context_emit_update_sub_resource(struct wined3d_device_conte
         {
             unsigned int uv_height = format->uv_height;
             unsigned int uv_width = format->uv_width;
-            const struct wined3d_format *plane_format;
 
-            plane_format = wined3d_get_format(context->device->adapter, format->plane_formats[0], 0);
-            wined3d_format_copy_data(plane_format, data, row_pitch, slice_pitch, map_desc.data, map_desc.row_pitch,
-                    map_desc.slice_pitch, box->right - box->left, box->bottom - box->top, box->back - box->front);
-            plane_format = wined3d_get_format(context->device->adapter, format->plane_formats[1], 0);
-            wined3d_format_copy_data(plane_format, (const uint8_t *)data + (row_pitch * (box->bottom - box->top)),
+            wined3d_format_copy_data(format->plane_formats[0], data, row_pitch, slice_pitch,
+                    map_desc.data, map_desc.row_pitch, map_desc.slice_pitch,
+                    box->right - box->left, box->bottom - box->top, box->back - box->front);
+            wined3d_format_copy_data(format->plane_formats[1],
+                    (const uint8_t *)data + (row_pitch * (box->bottom - box->top)),
                     row_pitch * 2 / uv_width, slice_pitch * 2 / uv_width / uv_height,
                     (uint8_t *)map_desc.data + map_desc.slice_pitch,
                     map_desc.row_pitch * 2 / uv_width, map_desc.slice_pitch * 2 / uv_width / uv_height,
@@ -3694,18 +3678,11 @@ struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device,
             free(cs->data);
             goto fail;
         }
-        if (!(cs->present_event = CreateEventW(NULL, FALSE, FALSE, NULL)))
-        {
-            ERR("Failed to create command stream present event.\n");
-            free(cs->data);
-            goto fail;
-        }
 
         if (!(GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
                 (const WCHAR *)wined3d_cs_run, &cs->wined3d_module)))
         {
             ERR("Failed to get wined3d module handle.\n");
-            CloseHandle(cs->present_event);
             if (cs->event)
                 CloseHandle(cs->event);
             free(cs->data);
@@ -3716,7 +3693,6 @@ struct wined3d_cs *wined3d_cs_create(struct wined3d_device *device,
         {
             ERR("Failed to create wined3d command stream thread.\n");
             FreeLibrary(cs->wined3d_module);
-            CloseHandle(cs->present_event);
             if (cs->event)
                 CloseHandle(cs->event);
             free(cs->data);
@@ -3740,8 +3716,6 @@ void wined3d_cs_destroy(struct wined3d_cs *cs)
     {
         wined3d_cs_emit_stop(cs);
         CloseHandle(cs->thread);
-        if (!CloseHandle(cs->present_event))
-            ERR("Closing present event failed.\n");
         if (cs->event && !CloseHandle(cs->event))
             ERR("Closing event failed.\n");
     }
diff --git a/dlls/wined3d/device.c b/dlls/wined3d/device.c
index 8f195c6dee4a..11934fccad2a 100644
--- a/dlls/wined3d/device.c
+++ b/dlls/wined3d/device.c
@@ -2741,7 +2741,7 @@ void CDECL wined3d_device_set_max_frame_latency(struct wined3d_device *device, u
 
     device->max_frame_latency = latency;
     for (i = 0; i < device->swapchain_count; ++i)
-        swapchain_set_max_frame_latency(device->swapchains[i], device);
+        wined3d_swapchain_set_max_frame_latency(device->swapchains[i], device->max_frame_latency);
 }
 
 unsigned int CDECL wined3d_device_get_max_frame_latency(const struct wined3d_device *device)
diff --git a/dlls/wined3d/swapchain.c b/dlls/wined3d/swapchain.c
index c26df770cdb9..06451c93d65f 100644
--- a/dlls/wined3d/swapchain.c
+++ b/dlls/wined3d/swapchain.c
@@ -174,6 +174,8 @@ ULONG CDECL wined3d_swapchain_decref(struct wined3d_swapchain *swapchain)
         if (swapchain->dc)
             wined3d_release_dc(swapchain->win_handle, swapchain->dc);
 
+        CloseHandle(swapchain->frame_latency_semaphore);
+
         swapchain->parent_ops->wined3d_object_destroyed(swapchain->parent);
         swapchain->device->adapter->adapter_ops->adapter_destroy_swapchain(swapchain);
 
@@ -1404,10 +1406,56 @@ static void wined3d_swapchain_apply_sample_count_override(const struct wined3d_s
     *quality = 0;
 }
 
-void swapchain_set_max_frame_latency(struct wined3d_swapchain *swapchain, const struct wined3d_device *device)
+HRESULT CDECL wined3d_swapchain_set_max_frame_latency(struct wined3d_swapchain *swapchain, unsigned int latency)
+{
+    TRACE("swapchain %p, latency %u.\n", swapchain, latency);
+
+    if (!(swapchain->state.desc.flags & WINED3D_SWAPCHAIN_FRAME_LATENCY_WAITABLE_OBJECT))
+        return WINED3DERR_INVALIDCALL;
+
+    if (!latency)
+        return WINED3DERR_INVALIDCALL;
+
+    if (latency > swapchain->max_frame_latency)
+    {
+        if (!ReleaseSemaphore(swapchain->frame_latency_semaphore, latency - swapchain->max_frame_latency, NULL))
+        {
+            ERR("Failed to release semaphore, error %lu.\n", GetLastError());
+            return HRESULT_FROM_WIN32(GetLastError());
+        }
+    }
+    swapchain->max_frame_latency = latency;
+    return WINED3D_OK;
+}
+
+HRESULT CDECL wined3d_swapchain_get_max_frame_latency(struct wined3d_swapchain *swapchain, unsigned int *latency)
 {
-    /* Subtract 1 for the implicit OpenGL latency. */
-    swapchain->max_frame_latency = device->max_frame_latency >= 2 ? device->max_frame_latency - 1 : 1;
+    TRACE("swapchain %p, latency %p.\n", swapchain, latency);
+
+    if (!(swapchain->state.desc.flags & WINED3D_SWAPCHAIN_FRAME_LATENCY_WAITABLE_OBJECT))
+        return WINED3DERR_INVALIDCALL;
+
+    *latency = swapchain->max_frame_latency;
+    return WINED3D_OK;
+}
+
+HANDLE CDECL wined3d_swapchain_get_frame_latency_waitable_object(struct wined3d_swapchain *swapchain)
+{
+    HANDLE handle;
+
+    TRACE("swapchain %p.\n", swapchain);
+
+    if (!(swapchain->state.desc.flags & WINED3D_SWAPCHAIN_FRAME_LATENCY_WAITABLE_OBJECT))
+        return NULL;
+
+    if (!DuplicateHandle(GetCurrentProcess(), swapchain->frame_latency_semaphore, GetCurrentProcess(),
+            &handle, 0, FALSE, DUPLICATE_SAME_ACCESS))
+    {
+        ERR("Failed to duplicate handle, error %lu.\n", GetLastError());
+        return NULL;
+    }
+
+    return handle;
 }
 
 static enum wined3d_format_id adapter_format_from_backbuffer_format(const struct wined3d_adapter *adapter,
@@ -1584,7 +1632,16 @@ static HRESULT wined3d_swapchain_init(struct wined3d_swapchain *swapchain, struc
     swapchain->ref = 1;
     swapchain->win_handle = window;
     swapchain->swap_interval = WINED3D_SWAP_INTERVAL_DEFAULT;
-    swapchain_set_max_frame_latency(swapchain, device);
+    if (desc->flags & WINED3D_SWAPCHAIN_FRAME_LATENCY_WAITABLE_OBJECT)
+        swapchain->max_frame_latency = 1;
+    else
+        swapchain->max_frame_latency = device->max_frame_latency;
+
+    if (!(swapchain->frame_latency_semaphore = CreateSemaphoreW(NULL, swapchain->max_frame_latency, LONG_MAX, NULL)))
+    {
+        ERR("Failed to create frame latency semaphore, error %lu.\n", GetLastError());
+        return HRESULT_FROM_WIN32(GetLastError());
+    }
 
     if (!(swapchain->dc = GetDCEx(swapchain->win_handle, 0, DCX_USESTYLE | DCX_CACHE)))
         WARN("Failed to retrieve device context, trying swapchain backup.\n");
@@ -1722,6 +1779,8 @@ err:
     if (swapchain->dc)
         wined3d_release_dc(swapchain->win_handle, swapchain->dc);
 
+    CloseHandle(swapchain->frame_latency_semaphore);
+
     wined3d_swapchain_state_cleanup(&swapchain->state);
     wined3d_mutex_unlock();
 
diff --git a/dlls/wined3d/texture_vk.c b/dlls/wined3d/texture_vk.c
index cedf068319c5..9a0862e738ca 100644
--- a/dlls/wined3d/texture_vk.c
+++ b/dlls/wined3d/texture_vk.c
@@ -328,12 +328,11 @@ static void wined3d_texture_vk_upload_data(struct wined3d_context *context,
     if (src_format->attrs & WINED3D_FORMAT_ATTR_PLANAR)
     {
         struct wined3d_const_bo_address uv_bo_addr;
-        const struct wined3d_format *plane_format;
         struct wined3d_box uv_box;
 
-        plane_format = wined3d_get_format(context->device->adapter, src_format->plane_formats[0], 0);
-        wined3d_texture_vk_upload_plane(context, VK_IMAGE_ASPECT_PLANE_0_BIT, src_bo_addr, plane_format, src_box,
-                src_row_pitch, src_slice_pitch, dst_texture, dst_sub_resource_idx, dst_x, dst_y, dst_z);
+        wined3d_texture_vk_upload_plane(context, VK_IMAGE_ASPECT_PLANE_0_BIT,
+                src_bo_addr, src_format->plane_formats[0], src_box, src_row_pitch, src_slice_pitch,
+                dst_texture, dst_sub_resource_idx, dst_x, dst_y, dst_z);
 
         uv_bo_addr = *src_bo_addr;
         uv_bo_addr.addr += src_slice_pitch;
@@ -347,9 +346,9 @@ static void wined3d_texture_vk_upload_data(struct wined3d_context *context,
         src_row_pitch = src_row_pitch * 2 / src_format->uv_width;
         src_slice_pitch = src_slice_pitch * 2 / src_format->uv_width / src_format->uv_height;
 
-        plane_format = wined3d_get_format(context->device->adapter, src_format->plane_formats[1], 0);
-        wined3d_texture_vk_upload_plane(context, VK_IMAGE_ASPECT_PLANE_1_BIT, &uv_bo_addr, plane_format, &uv_box,
-                src_row_pitch, src_slice_pitch, dst_texture, dst_sub_resource_idx, dst_x, dst_y, dst_z);
+        wined3d_texture_vk_upload_plane(context, VK_IMAGE_ASPECT_PLANE_1_BIT, &uv_bo_addr,
+                src_format->plane_formats[1], &uv_box, src_row_pitch, src_slice_pitch,
+                dst_texture, dst_sub_resource_idx, dst_x, dst_y, dst_z);
     }
     else
     {
@@ -626,13 +625,12 @@ static void wined3d_texture_vk_download_data(struct wined3d_context *context,
 
     if (dst_format->attrs & WINED3D_FORMAT_ATTR_PLANAR)
     {
-        const struct wined3d_format *plane_format;
         struct wined3d_bo_address uv_bo_addr;
         struct wined3d_box uv_box;
 
-        plane_format = wined3d_get_format(context->device->adapter, dst_format->plane_formats[0], 0);
-        wined3d_texture_vk_download_plane(context, VK_IMAGE_ASPECT_PLANE_0_BIT, src_texture, src_sub_resource_idx,
-                src_box, dst_bo_addr, plane_format, dst_x, dst_y, dst_z, dst_row_pitch, dst_slice_pitch);
+        wined3d_texture_vk_download_plane(context, VK_IMAGE_ASPECT_PLANE_0_BIT,
+                src_texture, src_sub_resource_idx, src_box,
+                dst_bo_addr, dst_format->plane_formats[0], dst_x, dst_y, dst_z, dst_row_pitch, dst_slice_pitch);
 
         uv_bo_addr = *dst_bo_addr;
         uv_bo_addr.addr += dst_slice_pitch;
@@ -646,9 +644,9 @@ static void wined3d_texture_vk_download_data(struct wined3d_context *context,
         dst_row_pitch = dst_row_pitch * 2 / dst_format->uv_width;
         dst_slice_pitch = dst_slice_pitch * 2 / dst_format->uv_width / dst_format->uv_height;
 
-        plane_format = wined3d_get_format(context->device->adapter, dst_format->plane_formats[1], 0);
-        wined3d_texture_vk_download_plane(context, VK_IMAGE_ASPECT_PLANE_1_BIT, src_texture, src_sub_resource_idx,
-                &uv_box, &uv_bo_addr, plane_format, dst_x, dst_y, dst_z, dst_row_pitch, dst_slice_pitch);
+        wined3d_texture_vk_download_plane(context, VK_IMAGE_ASPECT_PLANE_1_BIT,
+                src_texture, src_sub_resource_idx, &uv_box,
+                &uv_bo_addr, dst_format->plane_formats[1], dst_x, dst_y, dst_z, dst_row_pitch, dst_slice_pitch);
     }
     else
     {
diff --git a/dlls/wined3d/utils.c b/dlls/wined3d/utils.c
index 0fb90ba3f18f..335ac6b8ac7f 100644
--- a/dlls/wined3d/utils.c
+++ b/dlls/wined3d/utils.c
@@ -2108,8 +2108,8 @@ static void init_format_plane_info(struct wined3d_adapter *adapter)
         struct wined3d_format *format = get_format_internal(adapter, format_plane_info[i].id);
 
         format->attrs |= WINED3D_FORMAT_ATTR_PLANAR;
-        format->plane_formats[0] = format_plane_info[i].plane_formats[0];
-        format->plane_formats[1] = format_plane_info[i].plane_formats[1];
+        format->plane_formats[0] = get_format_internal(adapter, format_plane_info[i].plane_formats[0]);
+        format->plane_formats[1] = get_format_internal(adapter, format_plane_info[i].plane_formats[1]);
         format->uv_width = format_plane_info[i].uv_width;
         format->uv_height = format_plane_info[i].uv_height;
     }
@@ -4263,7 +4263,7 @@ static void init_vulkan_format_info(struct wined3d_adapter *adapter, struct wine
 
         caps = ~0u;
         for (unsigned int i = 0; i < 2; ++i)
-            caps &= get_format_internal(adapter, format->f.plane_formats[i])->caps[WINED3D_GL_RES_TYPE_TEX_2D];
+            caps &= format->f.plane_formats[i]->caps[WINED3D_GL_RES_TYPE_TEX_2D];
         format->f.caps[WINED3D_GL_RES_TYPE_TEX_2D] |= caps;
         TRACE("Caps %#08x are supported for format %s.\n", caps, debug_d3dformat(format->f.id));
         return;
diff --git a/dlls/wined3d/view.c b/dlls/wined3d/view.c
index a506b0c6f18c..8f9e60daf99a 100644
--- a/dlls/wined3d/view.c
+++ b/dlls/wined3d/view.c
@@ -84,12 +84,12 @@ static GLenum get_texture_view_target(const struct wined3d_gl_info *gl_info,
 static bool find_format_plane_idx(const struct wined3d_format *resource_format,
         const struct wined3d_format *plane_format, unsigned int *plane_idx)
 {
-    if (plane_format->id == resource_format->plane_formats[0])
+    if (plane_format->typeless_id == resource_format->plane_formats[0]->typeless_id)
     {
         *plane_idx = 0;
         return true;
     }
-    if (plane_format->id == resource_format->plane_formats[1])
+    if (plane_format->typeless_id == resource_format->plane_formats[1]->typeless_id)
     {
         *plane_idx = 1;
         return true;
diff --git a/dlls/wined3d/wined3d.spec b/dlls/wined3d/wined3d.spec
index 735957c8777d..4f40e55f1bd1 100644
--- a/dlls/wined3d/wined3d.spec
+++ b/dlls/wined3d/wined3d.spec
@@ -300,9 +300,11 @@
 @ cdecl wined3d_swapchain_get_desc(ptr ptr)
 @ cdecl wined3d_swapchain_get_device(ptr)
 @ cdecl wined3d_swapchain_get_display_mode(ptr ptr ptr)
+@ cdecl wined3d_swapchain_get_frame_latency_waitable_object(ptr)
 @ cdecl wined3d_swapchain_get_front_buffer(ptr)
 @ cdecl wined3d_swapchain_get_front_buffer_data(ptr ptr long)
 @ cdecl wined3d_swapchain_get_gamma_ramp(ptr ptr)
+@ cdecl wined3d_swapchain_get_max_frame_latency(ptr long)
 @ cdecl wined3d_swapchain_get_parent(ptr)
 @ cdecl wined3d_swapchain_get_raster_status(ptr ptr)
 @ cdecl wined3d_swapchain_get_state(ptr)
@@ -310,6 +312,7 @@
 @ cdecl wined3d_swapchain_present(ptr ptr ptr ptr long long)
 @ cdecl wined3d_swapchain_resize_buffers(ptr long long long long long long)
 @ cdecl wined3d_swapchain_set_gamma_ramp(ptr long ptr)
+@ cdecl wined3d_swapchain_set_max_frame_latency(ptr long)
 @ cdecl wined3d_swapchain_set_palette(ptr ptr)
 @ cdecl wined3d_swapchain_set_window(ptr ptr)
 
diff --git a/dlls/wined3d/wined3d_private.h b/dlls/wined3d/wined3d_private.h
index e4e0cf24dc7c..1b8368aa3bab 100644
--- a/dlls/wined3d/wined3d_private.h
+++ b/dlls/wined3d/wined3d_private.h
@@ -3698,10 +3698,8 @@ struct wined3d_cs
     struct list query_poll_list;
     BOOL queries_flushed;
 
-    HANDLE event, present_event;
+    HANDLE event;
     LONG waiting_for_event;
-    LONG waiting_for_present;
-    LONG pending_presents;
 };
 
 static inline void wined3d_device_context_lock(struct wined3d_device_context *context)
@@ -4111,6 +4109,7 @@ struct wined3d_swapchain
     RECT front_buffer_update;
     unsigned int swap_interval;
     unsigned int max_frame_latency;
+    HANDLE frame_latency_semaphore;
 
     /* Performance tracking */
     LARGE_INTEGER last_present_time;
@@ -4605,7 +4604,7 @@ struct wined3d_format
     UINT block_height;
     UINT block_byte_count;
 
-    enum wined3d_format_id plane_formats[2];
+    struct wined3d_format *plane_formats[2];
     unsigned int uv_width, uv_height;
 
     enum wined3d_ffp_emit_idx emit_idx;
diff --git a/dlls/winemac.drv/mouse.c b/dlls/winemac.drv/mouse.c
index 19674b2d0993..51ae29ae84e7 100644
--- a/dlls/winemac.drv/mouse.c
+++ b/dlls/winemac.drv/mouse.c
@@ -130,35 +130,16 @@ static void send_mouse_input(HWND hwnd, macdrv_window cocoa_window, UINT flags,
                              DWORD mouse_data, BOOL drag, unsigned long time)
 {
     INPUT input;
-    HWND top_level_hwnd;
-
-    top_level_hwnd = NtUserGetAncestor(hwnd, GA_ROOT);
-
-    if ((flags & MOUSEEVENTF_MOVE) && (flags & MOUSEEVENTF_ABSOLUTE) && !drag &&
-        cocoa_window != macdrv_thread_data()->capture_window)
-    {
-        /* update the wine server Z-order */
-        SERVER_START_REQ(update_window_zorder)
-        {
-            req->window      = wine_server_user_handle(top_level_hwnd);
-            req->rect.left   = x;
-            req->rect.top    = y;
-            req->rect.right  = x + 1;
-            req->rect.bottom = y + 1;
-            wine_server_call(req);
-        }
-        SERVER_END_REQ;
-    }
 
     input.type              = INPUT_MOUSE;
     input.mi.dx             = x;
     input.mi.dy             = y;
     input.mi.mouseData      = mouse_data;
-    input.mi.dwFlags        = flags;
+    input.mi.dwFlags        = flags | MOUSEEVENTF_MOVE_NOCOALESCE;
     input.mi.time           = time;
     input.mi.dwExtraInfo    = 0;
 
-    NtUserSendHardwareInput(top_level_hwnd, 0, &input, 0);
+    NtUserSendHardwareInput(hwnd, 0, &input, 0);
 }
 
 
diff --git a/dlls/winemac.drv/opengl.c b/dlls/winemac.drv/opengl.c
index 01b4b03b029f..6b2ec0bc9d46 100644
--- a/dlls/winemac.drv/opengl.c
+++ b/dlls/winemac.drv/opengl.c
@@ -56,8 +56,8 @@ static struct gl_info gl_info;
 
 struct macdrv_context
 {
+    BOOL                    core;
     int                     format;
-    GLint                   renderer_id;
     macdrv_opengl_context   context;
     CGLContextObj           cglcontext;
     HWND                    draw_hwnd;
@@ -227,6 +227,7 @@ C_ASSERT(sizeof(((pixel_format_or_code*)0)->format) <= sizeof(((pixel_format_or_
 
 static pixel_format *pixel_formats;
 static int nb_formats, nb_displayable_formats;
+static CGLOpenGLProfile core_profile;
 
 
 static const char* debugstr_attrib(int attrib, int value)
@@ -1202,56 +1203,51 @@ static const pixel_format *get_pixel_format(int format, BOOL allow_nondisplayabl
     return NULL;
 }
 
-
-static BOOL init_gl_info(void)
+static CGLContextObj init_context(CGLOpenGLProfile profile)
 {
-    static const char legacy_extensions[] = " WGL_EXT_extensions_string";
-    static const char legacy_ext_swap_control[] = " WGL_EXT_swap_control";
-
-    CGDirectDisplayID display = CGMainDisplayID();
-    CGOpenGLDisplayMask displayMask = CGDisplayIDToOpenGLDisplayMask(display);
-    CGLPixelFormatAttribute attribs[] = {
-        kCGLPFADisplayMask, displayMask,
-        0
-    };
-    CGLPixelFormatAttribute core_attribs[] =
+    CGLPixelFormatAttribute attribs[] =
     {
-        kCGLPFADisplayMask, displayMask,
-        kCGLPFAAccelerated,
-        kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)kCGLOGLPVersion_3_2_Core,
-        0
+        kCGLPFADisplayMask, CGDisplayIDToOpenGLDisplayMask(CGMainDisplayID()),
+        kCGLPFAAccelerated, kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)profile, 0,
     };
     CGLPixelFormatObj pix;
-    GLint virtualScreens;
-    CGLError err;
     CGLContextObj context;
-    CGLContextObj old_context = CGLGetCurrentContext();
-    const char *str;
-    size_t length;
+    GLint screens;
+    CGLError err;
 
-    err = CGLChoosePixelFormat(attribs, &pix, &virtualScreens);
-    if (err != kCGLNoError || !pix)
+    if ((err = CGLChoosePixelFormat(attribs, &pix, &screens)) || !pix)
     {
         WARN("CGLChoosePixelFormat() failed with error %d %s\n", err, CGLErrorString(err));
-        return FALSE;
+        return NULL;
     }
-
-    err = CGLCreateContext(pix, NULL, &context);
-    CGLReleasePixelFormat(pix);
-    if (err != kCGLNoError || !context)
+    if ((err = CGLCreateContext(pix, NULL, &context)) || !context)
     {
         WARN("CGLCreateContext() failed with error %d %s\n", err, CGLErrorString(err));
-        return FALSE;
+        context = NULL;
     }
+    CGLReleasePixelFormat(pix);
 
-    err = CGLSetCurrentContext(context);
-    if (err != kCGLNoError)
+    if ((err = CGLSetCurrentContext(context)))
     {
         WARN("CGLSetCurrentContext() failed with error %d %s\n", err, CGLErrorString(err));
         CGLReleaseContext(context);
-        return FALSE;
+        return NULL;
     }
 
+    return context;
+}
+
+static BOOL init_gl_info(void)
+{
+    static const char legacy_extensions[] = " WGL_EXT_extensions_string";
+    static const char legacy_ext_swap_control[] = " WGL_EXT_swap_control";
+
+    CGLContextObj context;
+    CGLContextObj old_context = CGLGetCurrentContext();
+    const char *str;
+    size_t length;
+
+    if (!(context = init_context(kCGLOGLPVersion_Legacy))) return FALSE;
     str = (const char*)pglGetString(GL_EXTENSIONS);
     length = strlen(str) + sizeof(legacy_extensions);
     if (allow_vsync)
@@ -1271,38 +1267,23 @@ static BOOL init_gl_info(void)
 
     CGLSetCurrentContext(old_context);
     CGLReleaseContext(context);
+    core_profile = kCGLOGLPVersion_Legacy;
 
-    err = CGLChoosePixelFormat(core_attribs, &pix, &virtualScreens);
-    if (err != kCGLNoError || !pix)
-    {
-        WARN("CGLChoosePixelFormat() for a core context failed with error %d %s\n",
-             err, CGLErrorString(err));
-        return TRUE;
-    }
-
-    err = CGLCreateContext(pix, NULL, &context);
-    CGLReleasePixelFormat(pix);
-    if (err != kCGLNoError || !context)
-    {
-        WARN("CGLCreateContext() for a core context failed with error %d %s\n",
-             err, CGLErrorString(err));
-        return TRUE;
-    }
-
-    err = CGLSetCurrentContext(context);
-    if (err != kCGLNoError)
-    {
-        WARN("CGLSetCurrentContext() for a core context failed with error %d %s\n",
-             err, CGLErrorString(err));
-        CGLReleaseContext(context);
-        return TRUE;
-    }
+    if (!(context = init_context(kCGLOGLPVersion_GL3_Core))) return TRUE;
+    str = (const char*)pglGetString(GL_VERSION);
+    TRACE("GL3_Core context version: %s\n", str);
+    sscanf(str, "%u.%u", &gl_info.max_major, &gl_info.max_minor);
+    CGLSetCurrentContext(old_context);
+    CGLReleaseContext(context);
+    core_profile = kCGLOGLPVersion_GL3_Core;
 
+    if (!(context = init_context(kCGLOGLPVersion_GL4_Core))) return TRUE;
     str = (const char*)pglGetString(GL_VERSION);
-    TRACE("Core context GL version: %s\n", str);
+    TRACE("GL4_Core context version: %s\n", str);
     sscanf(str, "%u.%u", &gl_info.max_major, &gl_info.max_minor);
     CGLSetCurrentContext(old_context);
     CGLReleaseContext(context);
+    core_profile = kCGLOGLPVersion_GL4_Core;
 
     return TRUE;
 }
@@ -1311,7 +1292,7 @@ static BOOL init_gl_info(void)
 /**********************************************************************
  *              create_context
  */
-static BOOL create_context(struct macdrv_context *context, CGLContextObj share, unsigned int major)
+static BOOL create_context(struct macdrv_context *context, CGLContextObj share, BOOL *shared)
 {
     const pixel_format *pf;
     CGLPixelFormatAttribute attribs[64];
@@ -1319,7 +1300,6 @@ static BOOL create_context(struct macdrv_context *context, CGLContextObj share,
     CGLPixelFormatObj pix;
     GLint virtualScreens;
     CGLError err;
-    BOOL core = major >= 3;
 
     pf = get_pixel_format(context->format, TRUE /* non-displayable */);
     if (!pf)
@@ -1332,14 +1312,6 @@ static BOOL create_context(struct macdrv_context *context, CGLContextObj share,
     attribs[n++] = kCGLPFAMinimumPolicy;
     attribs[n++] = kCGLPFAClosestPolicy;
 
-    if (context->renderer_id)
-    {
-        attribs[n++] = kCGLPFARendererID;
-        attribs[n++] = context->renderer_id;
-        attribs[n++] = kCGLPFASingleRenderer;
-        attribs[n++] = kCGLPFANoRecovery;
-    }
-
     if (pf->accelerated)
     {
         attribs[n++] = kCGLPFAAccelerated;
@@ -1354,7 +1326,7 @@ static BOOL create_context(struct macdrv_context *context, CGLContextObj share,
     if (pf->double_buffer)
         attribs[n++] = kCGLPFADoubleBuffer;
 
-    if (!core)
+    if (!context->core)
     {
         attribs[n++] = kCGLPFAAuxBuffers;
         attribs[n++] = pf->aux_buffers;
@@ -1376,13 +1348,13 @@ static BOOL create_context(struct macdrv_context *context, CGLContextObj share,
     if (pf->stereo)
         attribs[n++] = kCGLPFAStereo;
 
-    if (pf->accum_mode && !core)
+    if (pf->accum_mode && !context->core)
     {
         attribs[n++] = kCGLPFAAccumSize;
         attribs[n++] = color_modes[pf->accum_mode - 1].color_bits;
     }
 
-    if (pf->pbuffer && !core)
+    if (pf->pbuffer && !context->core)
         attribs[n++] = kCGLPFAPBuffer;
 
     if (pf->sample_buffers && pf->samples)
@@ -1396,13 +1368,10 @@ static BOOL create_context(struct macdrv_context *context, CGLContextObj share,
     if (pf->backing_store)
         attribs[n++] = kCGLPFABackingStore;
 
-    if (core)
+    if (context->core)
     {
         attribs[n++] = kCGLPFAOpenGLProfile;
-        if (major == 3)
-            attribs[n++] = (int)kCGLOGLPVersion_GL3_Core;
-        else
-            attribs[n++] = (int)kCGLOGLPVersion_GL4_Core;
+        attribs[n++] = (CGLPixelFormatAttribute)core_profile;
     }
 
     attribs[n] = 0;
@@ -2098,14 +2067,13 @@ static UINT macdrv_pbuffer_bind(HDC hdc, struct opengl_drawable *base, GLenum so
  *
  * WGL_ARB_create_context: wglCreateContextAttribsARB
  */
-static BOOL macdrv_context_create(int format, void *shared, const int *attrib_list, void **private)
+static BOOL macdrv_context_create(int format, void *share, const int *attrib_list, void **private, BOOL *shared)
 {
-    struct macdrv_context *share_context = shared;
+    struct macdrv_context *share_context = share;
     struct macdrv_context *context;
     const int *iptr;
     int major = 1, minor = 0, profile = WGL_CONTEXT_CORE_PROFILE_BIT_ARB, flags = 0;
     BOOL core = FALSE;
-    GLint renderer_id = 0;
 
     TRACE("format %d, share_context %p, attrib_list %p\n", format, share_context, attrib_list);
 
@@ -2145,6 +2113,7 @@ static BOOL macdrv_context_create(int format, void *shared, const int *attrib_li
                     RtlSetLastWin32Error(ERROR_INVALID_PROFILE_ARB);
                     return FALSE;
                 }
+                core = value == WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
                 profile = value;
                 break;
 
@@ -2155,6 +2124,13 @@ static BOOL macdrv_context_create(int format, void *shared, const int *attrib_li
         }
     }
 
+    if (major > gl_info.max_major || (major == gl_info.max_major && minor > gl_info.max_minor))
+    {
+        WARN("Profile version %u.%u not supported\n", major, minor);
+        RtlSetLastWin32Error(ERROR_INVALID_VERSION_ARB);
+        return FALSE;
+    }
+
     if ((major == 3 && (minor == 2 || minor == 3)) ||
         (major == 4 && (minor == 0 || minor == 1)))
     {
@@ -2170,22 +2146,8 @@ static BOOL macdrv_context_create(int format, void *shared, const int *attrib_li
             RtlSetLastWin32Error(ERROR_INVALID_PROFILE_ARB);
             return FALSE;
         }
-        if (major > gl_info.max_major ||
-            (major == gl_info.max_major && minor > gl_info.max_minor))
-        {
-            WARN("This GL implementation does not support the requested GL version %u.%u\n",
-                 major, minor);
-            RtlSetLastWin32Error(ERROR_INVALID_PROFILE_ARB);
-            return FALSE;
-        }
         core = TRUE;
     }
-    else if (major >= 3)
-    {
-        WARN("Profile version %u.%u not supported\n", major, minor);
-        RtlSetLastWin32Error(ERROR_INVALID_VERSION_ARB);
-        return FALSE;
-    }
     else if (major < 1 || (major == 1 && (minor < 0 || minor > 5)) ||
              (major == 2 && (minor < 0 || minor > 1)))
     {
@@ -2200,11 +2162,18 @@ static BOOL macdrv_context_create(int format, void *shared, const int *attrib_li
         return FALSE;
     }
 
-    if (!(context = calloc(1, sizeof(*context)))) return FALSE;
+    if (share_context && share_context->core != core)
+    {
+        WARN("Cannot share core context with compatibility context\n");
+        share_context = NULL;
+        *shared = FALSE;
+    }
 
+    if (!(context = calloc(1, sizeof(*context)))) return FALSE;
+    context->core = core;
     context->format = format;
-    context->renderer_id = renderer_id;
-    if (!create_context(context, share_context ? share_context->cglcontext : NULL, major))
+
+    if (!create_context(context, share_context ? share_context->cglcontext : NULL, shared))
     {
         free(context);
         return FALSE;
diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c
index 61f14ad496b2..c0384f3f0d55 100644
--- a/dlls/winemac.drv/window.c
+++ b/dlls/winemac.drv/window.c
@@ -1105,16 +1105,11 @@ static void macdrv_client_surface_update(struct client_surface *client)
     struct macdrv_client_surface *surface = impl_from_client_surface(client);
     HWND hwnd = client->hwnd, toplevel = NtUserGetAncestor(hwnd, GA_ROOT);
     struct macdrv_win_data *data;
-    RECT rect;
 
     TRACE("%s\n", debugstr_client_surface(client));
 
-    NtUserGetClientRect(hwnd, &rect, NtUserGetWinMonitorDpi(hwnd, MDT_RAW_DPI));
-    NtUserMapWindowPoints(hwnd, toplevel, (POINT *)&rect, 2, NtUserGetWinMonitorDpi(toplevel, MDT_RAW_DPI));
-
     if (!(data = get_win_data(toplevel))) return;
-    OffsetRect(&rect, data->rects.client.left - data->rects.visible.left, data->rects.client.top - data->rects.visible.top);
-    macdrv_set_view_frame(surface->cocoa_view, cgrect_from_rect(rect));
+    macdrv_set_view_frame(surface->cocoa_view, cgrect_from_rect(client->monitor_rect));
     macdrv_set_view_superview(surface->cocoa_view, toplevel == hwnd ? NULL : data->client_view, data->cocoa_window, NULL, NULL);
     release_win_data(data);
 }
@@ -1152,15 +1147,10 @@ struct macdrv_client_surface *impl_from_client_surface(struct client_surface *cl
 
 struct client_surface *macdrv_CreateClientSurface(HWND hwnd, int pixel_format)
 {
-    HWND toplevel = NtUserGetAncestor(hwnd, GA_ROOT);
     struct macdrv_client_surface *surface;
-    RECT rect;
-
-    NtUserGetClientRect(hwnd, &rect, NtUserGetWinMonitorDpi(hwnd, MDT_RAW_DPI));
-    NtUserMapWindowPoints(hwnd, toplevel, (POINT *)&rect, 2, NtUserGetWinMonitorDpi(toplevel, MDT_RAW_DPI));
 
     surface = client_surface_create(sizeof(*surface), &macdrv_client_surface_funcs, hwnd);
-    surface->cocoa_view = macdrv_create_view(cgrect_from_rect(rect));
+    surface->cocoa_view = macdrv_create_view(cgrect_from_rect(surface->client.monitor_rect));
     macdrv_set_view_hidden(surface->cocoa_view, TRUE);
 
     if (surface)
diff --git a/dlls/winewayland.drv/wayland.c b/dlls/winewayland.drv/wayland.c
index 5b2b0c5b25bc..d5fb36417c16 100644
--- a/dlls/winewayland.drv/wayland.c
+++ b/dlls/winewayland.drv/wayland.c
@@ -297,6 +297,8 @@ BOOL wayland_process_init(void)
         return FALSE;
     }
 
+    wayland_window_init();
+
     /* Populate registry */
     wl_registry_add_listener(process_wayland.wl_registry, &registry_listener, NULL);
 
diff --git a/dlls/winewayland.drv/wayland_pointer.c b/dlls/winewayland.drv/wayland_pointer.c
index af404994a440..0c190a9a48ab 100644
--- a/dlls/winewayland.drv/wayland_pointer.c
+++ b/dlls/winewayland.drv/wayland_pointer.c
@@ -32,6 +32,7 @@
 #define OEMRESOURCE
 
 #include "waylanddrv.h"
+#include "wine/server.h"
 #include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(cursor);
@@ -161,7 +162,7 @@ static void pointer_handle_motion_internal(wl_fixed_t sx, wl_fixed_t sy)
           hwnd, wl_fixed_to_double(sx), wl_fixed_to_double(sy),
           screen.x, screen.y);
 
-    NtUserSendHardwareInput(hwnd, 0, &input, 0);
+    NtUserSendHardwareInput(hwnd, SEND_HWMSG_RAWINPUT, &input, 0);
 }
 
 static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
@@ -170,7 +171,7 @@ static void pointer_handle_motion(void *data, struct wl_pointer *wl_pointer,
     struct wayland_pointer *pointer = &process_wayland.pointer;
 
     /* Ignore absolute motion events if in relative mode. */
-    if (pointer->zwp_relative_pointer_v1) return;
+    if (pointer->relative_mode) return;
 
     pointer_handle_motion_internal(sx, sy);
 }
@@ -266,7 +267,7 @@ static void pointer_handle_button(void *data, struct wl_pointer *wl_pointer,
 
     TRACE("hwnd=%p button=%#x state=%u\n", hwnd, button, state);
 
-    NtUserSendHardwareInput(hwnd, 0, &input, 0);
+    NtUserSendHardwareInput(hwnd, SEND_HWMSG_RAWINPUT, &input, 0);
 }
 
 static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
@@ -276,6 +277,8 @@ static void pointer_handle_axis(void *data, struct wl_pointer *wl_pointer,
 
 static void pointer_handle_frame(void *data, struct wl_pointer *wl_pointer)
 {
+    static const INPUT input = { .type = INPUT_MOUSE, .mi.dwFlags = MOUSEEVENTF_MOVE_NOCOALESCE };
+    NtUserSendHardwareInput(NULL, SEND_HWMSG_RAWINPUT, &input, 0); /* flush win32u accumulated motion */
 }
 
 static void pointer_handle_axis_source(void *data, struct wl_pointer *wl_pointer,
@@ -313,7 +316,7 @@ static void pointer_handle_axis_value120(void *data, struct wl_pointer *wl_point
 
     TRACE("hwnd=%p axis=%u value120=%d\n", hwnd, axis, value120);
 
-    NtUserSendHardwareInput(hwnd, 0, &input, 0);
+    NtUserSendHardwareInput(hwnd, SEND_HWMSG_RAWINPUT, &input, 0);
 }
 
 static void pointer_handle_axis_discrete(void *data, struct wl_pointer *wl_pointer,
@@ -357,7 +360,9 @@ static void relative_pointer_v1_relative_motion(void *private,
                                                 wl_fixed_t dx, wl_fixed_t dy,
                                                 wl_fixed_t dx_unaccel, wl_fixed_t dy_unaccel)
 {
-    INPUT input = {0};
+    const POINT raw_pos = { .x = wl_fixed_to_double(dx_unaccel), .y = wl_fixed_to_double(dy_unaccel) };
+    struct raw_mouse raw = { .count = 1, .data = { raw_pos } };
+    INPUT input = { .type = INPUT_MOUSE };
     HWND hwnd;
     struct wayland_win_data *data;
     double screen_x = 0.0, screen_y = 0.0;
@@ -374,24 +379,26 @@ static void relative_pointer_v1_relative_motion(void *private,
 
     pthread_mutex_lock(&pointer->mutex);
 
-    pointer->accum_x += screen_x;
-    pointer->accum_y += screen_y;
+    if (pointer->relative_mode)
+    {
+        pointer->accum_x += screen_x;
+        pointer->accum_y += screen_y;
 
-    input.type = INPUT_MOUSE;
-    input.mi.dx = round(pointer->accum_x);
-    input.mi.dy = round(pointer->accum_y);
-    input.mi.dwFlags = MOUSEEVENTF_MOVE;
+        input.mi.dx = round(pointer->accum_x);
+        input.mi.dy = round(pointer->accum_y);
+        input.mi.dwFlags = MOUSEEVENTF_MOVE;
 
-    pointer->accum_x -= input.mi.dx;
-    pointer->accum_y -= input.mi.dy;
+        pointer->accum_x -= input.mi.dx;
+        pointer->accum_y -= input.mi.dy;
+    }
 
     pthread_mutex_unlock(&pointer->mutex);
 
-    TRACE("hwnd=%p wayland_dxdy=%.2f,%.2f accum_dxdy=%d,%d\n",
-          hwnd, wl_fixed_to_double(dx), wl_fixed_to_double(dy),
-          input.mi.dx, input.mi.dy);
+    TRACE("hwnd=%p wayland_dxdy=%.2f,%.2f accum_dxdy=%d,%d wayland_raw=%.2f,%.2f raw_dxdy=%d,%d\n",
+          hwnd, wl_fixed_to_double(dx), wl_fixed_to_double(dy), input.mi.dx, input.mi.dy,
+          wl_fixed_to_double(dx_unaccel), wl_fixed_to_double(dy_unaccel), raw_pos.x, raw_pos.y);
 
-    NtUserSendHardwareInput(hwnd, 0, &input, 0);
+    NtUserSendHardwareInput(hwnd, SEND_HWMSG_RAWINPUT, &input, (LPARAM)&raw);
 }
 
 static const struct zwp_relative_pointer_v1_listener relative_pointer_v1_listener =
@@ -409,6 +416,18 @@ void wayland_pointer_init(struct wl_pointer *wl_pointer)
     pointer->enter_serial = 0;
     pthread_mutex_unlock(&pointer->mutex);
     wl_pointer_add_listener(pointer->wl_pointer, &pointer_listener, NULL);
+
+    if (!process_wayland.zwp_relative_pointer_manager_v1)
+        ERR("zwp_relative_pointer_manager_v1 isn't supported, skipping relative motion\n");
+    else
+    {
+        pointer->zwp_relative_pointer_v1 =
+            zwp_relative_pointer_manager_v1_get_relative_pointer(
+                process_wayland.zwp_relative_pointer_manager_v1,
+                pointer->wl_pointer);
+        zwp_relative_pointer_v1_add_listener(pointer->zwp_relative_pointer_v1,
+                                             &relative_pointer_v1_listener, NULL);
+    }
 }
 
 void wayland_pointer_deinit(void)
@@ -833,31 +852,6 @@ static void wayland_surface_calc_confine(struct wayland_surface *surface,
     *confine = map_rect_to_surface(surface, window_clip);
 }
 
-/**********************************************************************
- *          wayland_surface_client_covers_vscreen
- *
- * Whether a surface window client area covers the whole virtual screen.
- */
-static BOOL wayland_surface_client_covers_vscreen(struct wayland_surface *surface)
-{
-    RECT vscreen_rect, rect;
-
-    /* Get individual system metrics to get coords in thread dpi
-     * (NtUserGetVirtualScreenRect would return values in system dpi). */
-    vscreen_rect.left = NtUserGetSystemMetrics(SM_XVIRTUALSCREEN);
-    vscreen_rect.top = NtUserGetSystemMetrics(SM_YVIRTUALSCREEN);
-    vscreen_rect.right = vscreen_rect.left +
-                         NtUserGetSystemMetrics(SM_CXVIRTUALSCREEN);
-    vscreen_rect.bottom = vscreen_rect.top +
-                          NtUserGetSystemMetrics(SM_CYVIRTUALSCREEN);
-
-    /* FIXME: surface->window.client_rect is in window dpi, whereas
-     * vscreen_rect is in thread dpi. */
-    intersect_rect(&rect, &surface->window.client_rect, &vscreen_rect);
-
-    return EqualRect(&vscreen_rect, &rect);
-}
-
 /***********************************************************************
  *           wayland_pointer_update_constraint
  *
@@ -865,7 +859,6 @@ static BOOL wayland_surface_client_covers_vscreen(struct wayland_surface *surfac
  */
 static void wayland_pointer_update_constraint(struct wl_surface *wl_surface,
                                               RECT *confine_rect,
-                                              BOOL covers_vscreen,
                                               BOOL force_lock)
 {
     struct wayland_pointer *pointer = &process_wayland.pointer;
@@ -881,7 +874,7 @@ static void wayland_pointer_update_constraint(struct wl_surface *wl_surface,
 
     is_visible = pointer->cursor.wl_surface || pointer->wp_cursor_shape_device_v1;
     needs_lock = wl_surface &&
-                 (((confine_rect || covers_vscreen) && !is_visible) || force_lock) &&
+                 ((confine_rect && !is_visible) || force_lock) &&
                  pointer->wl_pointer;
     needs_confine = wl_surface && confine_rect && is_visible && !force_lock &&
                     pointer->wl_pointer;
@@ -959,38 +952,25 @@ static void wayland_pointer_update_constraint(struct wl_surface *wl_surface,
         }
     }
 
-    if (!process_wayland.zwp_relative_pointer_manager_v1)
-    {
-        if (!once++)
-            ERR("zwp_relative_pointer_manager_v1 isn't supported, skipping relative motion\n");
-        return;
-    }
-
     needs_relative = !is_visible && pointer->constraint_hwnd &&
                      pointer->constraint_hwnd == pointer->focused_hwnd;
 
-    if (needs_relative && !pointer->zwp_relative_pointer_v1)
+    if (needs_relative && !pointer->relative_mode && pointer->zwp_relative_pointer_v1)
     {
+        pointer->relative_mode = TRUE;
         pointer->accum_x = pointer->accum_y = 0;
-        pointer->zwp_relative_pointer_v1 =
-            zwp_relative_pointer_manager_v1_get_relative_pointer(
-                process_wayland.zwp_relative_pointer_manager_v1,
-                pointer->wl_pointer);
-        zwp_relative_pointer_v1_add_listener(pointer->zwp_relative_pointer_v1,
-                                             &relative_pointer_v1_listener, NULL);
         TRACE("Enabling relative motion\n");
     }
-    else if (!needs_relative && pointer->zwp_relative_pointer_v1)
+    else if (!needs_relative && pointer->relative_mode)
     {
-        zwp_relative_pointer_v1_destroy(pointer->zwp_relative_pointer_v1);
-        pointer->zwp_relative_pointer_v1 = NULL;
+        pointer->relative_mode = FALSE;
         TRACE("Disabling relative motion\n");
     }
 }
 
 void wayland_pointer_clear_constraint(void)
 {
-    wayland_pointer_update_constraint(NULL, NULL, FALSE, FALSE);
+    wayland_pointer_update_constraint(NULL, NULL, FALSE);
 }
 
 /***********************************************************************
@@ -1011,7 +991,7 @@ BOOL WAYLAND_SetCursorPos(INT x, INT y)
     struct wayland_pointer *pointer = &process_wayland.pointer;
 
     pthread_mutex_lock(&pointer->mutex);
-    if (pointer->zwp_relative_pointer_v1)
+    if (pointer->relative_mode)
     {
         pthread_mutex_unlock(&pointer->mutex);
         return FALSE;
@@ -1034,7 +1014,6 @@ BOOL WAYLAND_ClipCursor(const RECT *clip, BOOL reset)
     struct wl_surface *wl_surface = NULL;
     struct wayland_surface *surface = NULL;
     struct wayland_win_data *data;
-    BOOL covers_vscreen = FALSE;
     RECT confine_rect;
     POINT cursor_pos, warp;
 
@@ -1048,7 +1027,6 @@ BOOL WAYLAND_ClipCursor(const RECT *clip, BOOL reset)
     {
         wl_surface = surface->wl_surface;
         if (clip) wayland_surface_calc_confine(surface, clip, &confine_rect);
-        covers_vscreen = wayland_surface_client_covers_vscreen(surface);
         warp.x = cursor_pos.x - surface->window.rect.left;
         warp.y = cursor_pos.y - surface->window.rect.top;
         warp = map_point_to_surface(surface, warp);
@@ -1072,7 +1050,7 @@ BOOL WAYLAND_ClipCursor(const RECT *clip, BOOL reset)
         }
         else
         {
-            wayland_pointer_update_constraint(wl_surface, NULL, FALSE, TRUE);
+            wayland_pointer_update_constraint(wl_surface, NULL, TRUE);
         }
         pointer->pending_warp = FALSE;
     }
@@ -1098,7 +1076,6 @@ BOOL WAYLAND_ClipCursor(const RECT *clip, BOOL reset)
     * so we can access it without having the win data lock. */
     wayland_pointer_update_constraint(wl_surface,
                                       (clip && wl_surface) ? &confine_rect : NULL,
-                                      covers_vscreen,
                                       FALSE);
     pthread_mutex_unlock(&pointer->mutex);
 
diff --git a/dlls/winewayland.drv/wayland_surface.c b/dlls/winewayland.drv/wayland_surface.c
index 0dae09221796..5ce2f445fea7 100644
--- a/dlls/winewayland.drv/wayland_surface.c
+++ b/dlls/winewayland.drv/wayland_surface.c
@@ -69,7 +69,7 @@ static void xdg_surface_handle_configure(void *private, struct xdg_surface *xdg_
      * able to flush before due to the lack of the initial configure. */
     if (initial_configure)
     {
-        NtUserExposeWindowSurface(hwnd, 0, NULL, 0);
+        NtUserExposeWindowSurface(hwnd, 0, NULL);
     }
 }
 
@@ -142,7 +142,6 @@ void wp_fractional_scale_handle_scale(void* user_data,
                                       uint32_t scale_fixed)
 {
     struct wayland_win_data *data;
-    struct wayland_client_surface *client;
     struct wayland_surface *surface;
     double scale = scale_fixed / 120.0;
     HWND hwnd = user_data;
@@ -158,9 +157,8 @@ void wp_fractional_scale_handle_scale(void* user_data,
 
     surface->window.scale = scale;
 
-    /* reattach the client surface as its rect has changed */
-    if ((client = data->client_surface))
-        wayland_client_surface_attach(client, client->toplevel);
+    /* reattach client surfaces as their rects have changed */
+    update_client_surfaces(hwnd);
 
     /* the subsurface rect has changed */
     if (surface->role == WAYLAND_SURFACE_ROLE_SUBSURFACE)
@@ -171,7 +169,7 @@ void wp_fractional_scale_handle_scale(void* user_data,
 
     wayland_win_data_release(data);
 
-    NtUserExposeWindowSurface(hwnd, 0, NULL, 0);
+    NtUserExposeWindowSurface(hwnd, 0, NULL);
 }
 
 static const struct wp_fractional_scale_v1_listener wp_fractional_scale_listener =
@@ -691,12 +689,9 @@ static void wayland_surface_reconfigure_client(struct wayland_surface *surface,
                                                struct wayland_client_surface *client,
                                                const RECT *client_rect)
 {
-    struct wayland_window_config *window = &surface->window;
-    RECT rect = *client_rect;
+    RECT rect = client_rect ? *client_rect : client->rect;
 
     /* The offset of the client area origin relatively to the window origin. */
-    OffsetRect(&rect, window->client_rect.left - window->rect.left,
-               window->client_rect.top - window->rect.top);
     rect = map_rect_to_surface(surface, rect);
 
     TRACE("hwnd=%p rect=%s\n", surface->hwnd, wine_dbgstr_rect(&rect));
@@ -711,6 +706,8 @@ static void wayland_surface_reconfigure_client(struct wayland_surface *surface,
         wp_viewport_set_destination(client->wp_viewport, rect.right - rect.left, rect.bottom - rect.top);
     else /* We can't have a 0x0 destination, use 1x1 instead. */
         wp_viewport_set_destination(client->wp_viewport, 1, 1);
+
+    client->rect = *client_rect;
 }
 
 /**********************************************************************
@@ -766,9 +763,10 @@ static void wayland_surface_reconfigure_subsurface(struct wayland_surface *surfa
     struct wayland_win_data *owner_data;
     struct wayland_surface *owner_surface;
 
-    if (surface->processing.serial && surface->processing.processed &&
-        (owner_data = wayland_win_data_get_nolock(surface->owner_hwnd)) &&
-        (owner_surface = owner_data->wayland_surface))
+    if (!surface->processing.serial || !surface->processing.processed) return;
+    if (!(owner_data = wayland_win_data_get(surface->owner_hwnd))) return;
+
+    if ((owner_surface = owner_data->wayland_surface))
     {
         RECT rect = surface->window.rect;
 
@@ -786,6 +784,8 @@ static void wayland_surface_reconfigure_subsurface(struct wayland_surface *surfa
 
         memset(&surface->processing, 0, sizeof(surface->processing));
     }
+
+    wayland_win_data_release(owner_data);
 }
 
 /**********************************************************************
@@ -1172,7 +1172,7 @@ static void wayland_client_surface_detach(struct client_surface *client)
     if ((data = wayland_win_data_get(client->hwnd)))
     {
         if (data->client_surface == surface) data->client_surface = NULL;
-        wayland_client_surface_attach(surface, NULL);
+        wayland_client_surface_attach(surface, NULL, NULL);
         wayland_win_data_release(data);
     }
 }
@@ -1180,15 +1180,17 @@ static void wayland_client_surface_detach(struct client_surface *client)
 static void wayland_client_surface_update(struct client_surface *client)
 {
     struct wayland_client_surface *surface = impl_from_client_surface(client);
-    HWND hwnd = client->hwnd, toplevel = NtUserGetAncestor(hwnd, GA_ROOT);
+    HWND hwnd = client->hwnd, toplevel = client->toplevel;
     struct wayland_win_data *data;
 
+    TRACE("%s\n", debugstr_client_surface(client));
+
     if (!(data = wayland_win_data_get(hwnd))) return;
 
     if (toplevel && NtUserIsWindowVisible(hwnd))
-        wayland_client_surface_attach(surface, toplevel);
+        wayland_client_surface_attach(surface, toplevel, &client->monitor_rect);
     else
-        wayland_client_surface_attach(surface, NULL);
+        wayland_client_surface_attach(surface, NULL, NULL);
 
     wayland_win_data_release(data);
 }
@@ -1196,8 +1198,28 @@ static void wayland_client_surface_update(struct client_surface *client)
 static void wayland_client_surface_present(struct client_surface *client, HDC hdc)
 {
     struct wayland_client_surface *surface = impl_from_client_surface(client);
-    HWND hwnd = client->hwnd, toplevel = NtUserGetAncestor(hwnd, GA_ROOT);
-    ensure_window_surface_contents(toplevel);
+    HWND hwnd = client->hwnd, toplevel = client->toplevel;
+    struct wayland_surface *wayland_surface;
+    struct wayland_win_data *data;
+
+    if (!(data = wayland_win_data_get(toplevel))) return;
+
+    if ((wayland_surface = data->wayland_surface))
+    {
+        wayland_surface_ensure_contents(wayland_surface);
+
+        /* Handle any processed configure request, to ensure the related
+         * surface state is applied by the compositor. */
+        if (wayland_surface->processing.serial &&
+            wayland_surface->processing.processed &&
+            wayland_surface_reconfigure(wayland_surface))
+        {
+            wl_surface_commit(wayland_surface->wl_surface);
+        }
+    }
+
+    wayland_win_data_release(data);
+
     set_client_surface(hwnd, surface);
 }
 
@@ -1257,12 +1279,10 @@ err:
     return NULL;
 }
 
-void wayland_client_surface_attach(struct wayland_client_surface *client, HWND toplevel)
+void wayland_client_surface_attach(struct wayland_client_surface *client, HWND toplevel, const RECT *rect)
 {
     struct wayland_win_data *toplevel_data;
     struct wayland_surface *surface;
-    HWND hwnd = client->client.hwnd;
-    RECT client_rect;
 
     if (!toplevel)
     {
@@ -1276,37 +1296,34 @@ void wayland_client_surface_attach(struct wayland_client_surface *client, HWND t
         return;
     }
 
-    if (!(toplevel_data = wayland_win_data_get_nolock(toplevel)) || !(surface = toplevel_data->wayland_surface))
+    if (!(toplevel_data = wayland_win_data_get(toplevel)) || !(surface = toplevel_data->wayland_surface))
     {
-        wayland_client_surface_attach(client, NULL);
-        return;
+        if (toplevel_data) wayland_win_data_release(toplevel_data);
+        return wayland_client_surface_attach(client, NULL, NULL);
     }
 
     if (client->toplevel != toplevel)
     {
-        wayland_client_surface_attach(client, NULL);
+        wayland_client_surface_attach(client, NULL, NULL);
 
         client->wl_subsurface =
             wl_subcompositor_get_subsurface(process_wayland.wl_subcompositor,
                                             client->wl_surface,
                                             surface->wl_surface);
-        if (!client->wl_subsurface)
-        {
-            ERR("Failed to create client wl_subsurface\n");
-            return;
-        }
+        if (!client->wl_subsurface) goto done;
+
         /* Present contents independently of the parent surface. */
         wl_subsurface_set_desync(client->wl_subsurface);
 
         client->toplevel = toplevel;
     }
 
-    NtUserGetClientRect(hwnd, &client_rect, NtUserGetWinMonitorDpi(hwnd, MDT_RAW_DPI));
-    NtUserMapWindowPoints(hwnd, toplevel, (POINT *)&client_rect, 2, NtUserGetWinMonitorDpi(hwnd, MDT_RAW_DPI));
-
-    wayland_surface_reconfigure_client(surface, client, &client_rect);
+    wayland_surface_reconfigure_client(surface, client, rect);
     /* Commit to apply subsurface positioning. */
     wl_surface_commit(surface->wl_surface);
+
+done:
+    wayland_win_data_release(toplevel_data);
 }
 
 static void dummy_buffer_release(void *data, struct wl_buffer *buffer)
diff --git a/dlls/winewayland.drv/wayland_text_input.c b/dlls/winewayland.drv/wayland_text_input.c
index 980f86728c84..5ed08578e28b 100644
--- a/dlls/winewayland.drv/wayland_text_input.c
+++ b/dlls/winewayland.drv/wayland_text_input.c
@@ -252,7 +252,7 @@ BOOL WAYLAND_SetIMECompositionRect(HWND hwnd, RECT rect)
     wayland_win_data_release(data);
 
     zwp_text_input_v3_set_cursor_rectangle(text_input->zwp_text_input_v3,
-            surface_rect.left, surface_rect.right, surface_rect.right - surface_rect.left,
+            surface_rect.left, surface_rect.top, surface_rect.right - surface_rect.left,
             surface_rect.bottom - surface_rect.top);
     zwp_text_input_v3_commit(text_input->zwp_text_input_v3);
 
diff --git a/dlls/winewayland.drv/waylanddrv.h b/dlls/winewayland.drv/waylanddrv.h
index e0df8e56bb5c..fa590eecd131 100644
--- a/dlls/winewayland.drv/waylanddrv.h
+++ b/dlls/winewayland.drv/waylanddrv.h
@@ -113,6 +113,7 @@ struct wayland_pointer
     struct wp_cursor_shape_device_v1 *wp_cursor_shape_device_v1;
     HWND focused_hwnd;
     HWND constraint_hwnd;
+    BOOL relative_mode;
     BOOL pending_warp;
     uint32_t enter_serial;
     uint32_t button_serial;
@@ -235,7 +236,6 @@ struct wayland_surface_config
 struct wayland_window_config
 {
     RECT rect;
-    RECT client_rect;
     enum wayland_surface_config_state state;
     /* The scale (i.e., normalized dpi) the window is rendering at. */
     double scale;
@@ -249,6 +249,7 @@ struct wayland_client_surface
 {
     struct client_surface client;
     HWND toplevel;
+    RECT rect;
     struct wl_surface *wl_surface;
     struct wl_subsurface *wl_subsurface;
     struct wp_viewport *wp_viewport;
@@ -337,7 +338,7 @@ RECT map_rect_to_surface(struct wayland_surface *surface, RECT rect);
 POINT map_point_to_surface(struct wayland_surface *surface, POINT point);
 RECT map_rect_from_surface(struct wayland_surface *surface, RECT rect);
 POINT map_point_from_surface(struct wayland_surface *surface, POINT point);
-void wayland_client_surface_attach(struct wayland_client_surface *client, HWND toplevel);
+void wayland_client_surface_attach(struct wayland_client_surface *client, HWND toplevel, const RECT *rect);
 void wayland_surface_ensure_contents(struct wayland_surface *surface);
 void wayland_surface_set_title(struct wayland_surface *surface, LPCWSTR title);
 void wayland_surface_assign_icon(struct wayland_surface *surface);
@@ -385,14 +386,13 @@ struct wayland_win_data
 };
 
 struct wayland_win_data *wayland_win_data_get(HWND hwnd);
-struct wayland_win_data *wayland_win_data_get_nolock(HWND hwnd);
 void wayland_win_data_release(struct wayland_win_data *data);
 
 struct wayland_client_surface *get_client_surface(HWND hwnd);
 void set_client_surface(HWND hwnd, struct wayland_client_surface *client);
 BOOL set_window_surface_contents(HWND hwnd, struct wayland_shm_buffer *shm_buffer, HRGN damage_region);
 struct wayland_shm_buffer *get_window_surface_contents(HWND hwnd);
-void ensure_window_surface_contents(HWND hwnd);
+void wayland_window_init(void);
 
 /**********************************************************************
  *          Wayland Keyboard
diff --git a/dlls/winewayland.drv/window.c b/dlls/winewayland.drv/window.c
index afef743755b0..c16eb1f4b3e5 100644
--- a/dlls/winewayland.drv/window.c
+++ b/dlls/winewayland.drv/window.c
@@ -48,7 +48,7 @@ static int wayland_win_data_cmp_rb(const void *key,
     return 0;
 }
 
-static pthread_mutex_t win_data_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t win_data_mutex;
 static struct rb_tree win_data_rb = { wayland_win_data_cmp_rb };
 
 /***********************************************************************
@@ -104,22 +104,6 @@ static void wayland_win_data_destroy(struct wayland_win_data *data)
     free(data);
 }
 
-/***********************************************************************
- *           wayland_win_data_get_nolock
- *
- * Return the data structure associated with a window. This function does
- * not lock the win_data_mutex, so it must be externally synchronized.
- */
-struct wayland_win_data *wayland_win_data_get_nolock(HWND hwnd)
-{
-    struct rb_entry *rb_entry;
-
-    if ((rb_entry = rb_get(&win_data_rb, hwnd)))
-        return RB_ENTRY_VALUE(rb_entry, struct wayland_win_data, entry);
-
-    return NULL;
-}
-
 /***********************************************************************
  *           wayland_win_data_get
  *
@@ -127,10 +111,11 @@ struct wayland_win_data *wayland_win_data_get_nolock(HWND hwnd)
  */
 struct wayland_win_data *wayland_win_data_get(HWND hwnd)
 {
-    struct wayland_win_data *data;
+    struct rb_entry *entry;
 
     pthread_mutex_lock(&win_data_mutex);
-    if ((data = wayland_win_data_get_nolock(hwnd))) return data;
+    if ((entry = rb_get(&win_data_rb, hwnd)))
+        return RB_ENTRY_VALUE(entry, struct wayland_win_data, entry);
     pthread_mutex_unlock(&win_data_mutex);
 
     return NULL;
@@ -154,7 +139,6 @@ static void wayland_win_data_get_config(struct wayland_win_data *data,
     DWORD style;
 
     conf->rect = data->rects.window;
-    conf->client_rect = data->rects.client;
     style = NtUserGetWindowLongW(data->hwnd, GWL_STYLE);
 
     TRACE("window=%s style=%#x\n", wine_dbgstr_rect(&conf->rect), style);
@@ -184,13 +168,12 @@ static void reapply_cursor_clipping(void)
 {
     RECT rect;
     UINT context = NtUserSetThreadDpiAwarenessContext(NTUSER_DPI_PER_MONITOR_AWARE);
-    if (NtUserGetClipCursor(&rect )) NtUserClipCursor(&rect);
+    if (NtUserGetClipCursor(&rect)) NtUserClipCursor(&rect);
     NtUserSetThreadDpiAwarenessContext(context);
 }
 
 static BOOL wayland_win_data_create_wayland_surface(struct wayland_win_data *data, struct wayland_surface *owner_surface)
 {
-    struct wayland_client_surface *client = data->client_surface;
     struct wayland_surface *surface;
     enum wayland_surface_role role;
     BOOL visible;
@@ -209,9 +192,12 @@ static BOOL wayland_win_data_create_wayland_surface(struct wayland_win_data *dat
     /* we can temporarily clear the role of a surface but cannot assign a different one after it's set */
     if ((surface = data->wayland_surface) && role && surface->role && surface->role != role)
     {
-        if (client) wayland_client_surface_attach(client, NULL);
-        wayland_surface_destroy(data->wayland_surface);
+        /* Make sure any attached client surface is detached before we destroy the surface.
+         * They will be reattached when win32u updates them again after WindowPosChanged.
+         */
         data->wayland_surface = NULL;
+        update_client_surfaces(data->hwnd);
+        wayland_surface_destroy(surface);
     }
 
     if (!(surface = data->wayland_surface) && !(surface = wayland_surface_create(data->hwnd))) return FALSE;
@@ -240,7 +226,6 @@ static BOOL wayland_win_data_create_wayland_surface(struct wayland_win_data *dat
         break;
     }
 
-    if (visible && client) wayland_client_surface_attach(client, data->hwnd);
     wayland_win_data_get_config(data, &surface->window);
 
     /* Size/position changes affect the effective pointer constraint, so update
@@ -448,7 +433,6 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, HWND owner_hint, UIN
 {
     HWND owner = NtUserGetAncestor(hwnd, GA_ROOT);
     struct wayland_surface *owner_surface;
-    struct wayland_client_surface *client;
     struct wayland_win_data *data, *owner_data;
     BOOL managed, fullscreen = swp_flags & WINE_SWP_FULLSCREEN;
 
@@ -460,7 +444,7 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, HWND owner_hint, UIN
     if (!(managed = is_window_managed(hwnd, swp_flags, fullscreen)) && surface) owner = owner_hint;
 
     if (!(data = wayland_win_data_get(hwnd))) return;
-    owner_data = owner && owner != hwnd ? wayland_win_data_get_nolock(owner) : NULL;
+    owner_data = owner && owner != hwnd ? wayland_win_data_get(owner) : NULL;
     owner_surface = owner_data ? owner_data->wayland_surface : NULL;
 
     data->rects = *new_rects;
@@ -470,14 +454,6 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, HWND owner_hint, UIN
 
     if (!surface)
     {
-        if ((client = data->client_surface))
-        {
-            if (owner && NtUserIsWindowVisible(hwnd))
-                wayland_client_surface_attach(client, owner);
-            else
-                wayland_client_surface_attach(client, NULL);
-        }
-
         if (data->wayland_surface)
         {
             wayland_surface_destroy(data->wayland_surface);
@@ -489,6 +465,7 @@ void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, HWND owner_hint, UIN
         wayland_win_data_update_wayland_state(data);
     }
 
+    if (owner_data) wayland_win_data_release(owner_data);
     wayland_win_data_release(data);
 }
 
@@ -829,7 +806,8 @@ void WAYLAND_UpdateLayeredWindow(HWND hwnd, BYTE alpha, UINT flags)
 
 void set_client_surface(HWND hwnd, struct wayland_client_surface *new_client)
 {
-    HWND toplevel = NtUserGetAncestor(hwnd, GA_ROOT);
+    HWND toplevel = new_client->client.toplevel;
+    RECT rect = new_client->client.monitor_rect;
     struct wayland_client_surface *old_client;
     struct wayland_win_data *data;
 
@@ -841,14 +819,14 @@ void set_client_surface(HWND hwnd, struct wayland_client_surface *new_client)
     if (new_client != data->client_surface)
     {
         if ((old_client = data->client_surface))
-            wayland_client_surface_attach(old_client, NULL);
+            wayland_client_surface_attach(old_client, NULL, NULL);
 
         if ((data->client_surface = new_client))
         {
             if (toplevel && NtUserIsWindowVisible(hwnd))
-                wayland_client_surface_attach(new_client, toplevel);
+                wayland_client_surface_attach(new_client, toplevel, &rect);
             else
-                wayland_client_surface_attach(new_client, NULL);
+                wayland_client_surface_attach(new_client, NULL, NULL);
         }
     }
 
@@ -901,26 +879,12 @@ struct wayland_shm_buffer *get_window_surface_contents(HWND hwnd)
     return shm_buffer;
 }
 
-void ensure_window_surface_contents(HWND hwnd)
+void wayland_window_init(void)
 {
-    struct wayland_surface *wayland_surface;
-    struct wayland_win_data *data;
+    pthread_mutexattr_t attr;
 
-    if (!(data = wayland_win_data_get(hwnd))) return;
-
-    if ((wayland_surface = data->wayland_surface))
-    {
-        wayland_surface_ensure_contents(wayland_surface);
-
-        /* Handle any processed configure request, to ensure the related
-         * surface state is applied by the compositor. */
-        if (wayland_surface->processing.serial &&
-            wayland_surface->processing.processed &&
-            wayland_surface_reconfigure(wayland_surface))
-        {
-            wl_surface_commit(wayland_surface->wl_surface);
-        }
-    }
-
-    wayland_win_data_release(data);
+    pthread_mutexattr_init(&attr);
+    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+    pthread_mutex_init(&win_data_mutex, &attr);
+    pthread_mutexattr_destroy(&attr);
 }
diff --git a/dlls/winex11.drv/event.c b/dlls/winex11.drv/event.c
index e96b573083db..38ad7f89d66a 100644
--- a/dlls/winex11.drv/event.c
+++ b/dlls/winex11.drv/event.c
@@ -172,7 +172,7 @@ static inline void free_event_data( XEvent *event )
 #endif
 }
 
-static void host_window_send_gravity_events( struct host_window *win, Display *display, unsigned long serial, XEvent *previous )
+static void host_window_send_gravity_events( struct host_window *win, Display *display, unsigned long serial )
 {
     XGravityEvent event = {.type = GravityNotify, .serial = serial, .display = display};
     unsigned int i;
@@ -187,13 +187,12 @@ static void host_window_send_gravity_events( struct host_window *win, Display *d
         event.y = rect.top;
         event.send_event = 0;
 
-        if (previous->type == ConfigureNotify && previous->xconfigure.window == event.window) continue;
         TRACE( "generating GravityNotify for window %lx, rect %s\n", event.window, wine_dbgstr_rect(&rect) );
         XPutBackEvent( event.display, (XEvent *)&event );
     }
 }
 
-static BOOL host_window_filter_event( XEvent *event, XEvent *previous )
+static BOOL host_window_filter_event( XEvent *event )
 {
     struct host_window *win;
 
@@ -210,7 +209,7 @@ static BOOL host_window_filter_event( XEvent *event, XEvent *previous )
         XReparentEvent *reparent = (XReparentEvent *)event;
         TRACE( "host window %p/%lx ReparentNotify, parent %lx\n", win, win->window, reparent->parent );
         host_window_set_parent( win, reparent->parent );
-        host_window_send_gravity_events( win, event->xany.display, event->xany.serial, previous );
+        host_window_send_gravity_events( win, event->xany.display, event->xany.serial );
         break;
     }
     case GravityNotify:
@@ -219,7 +218,7 @@ static BOOL host_window_filter_event( XEvent *event, XEvent *previous )
         OffsetRect( &win->rect, gravity->x - win->rect.left, gravity->y - win->rect.top );
         if (win->parent) win->rect = host_window_configure_child( win->parent, win->window, win->rect, FALSE );
         TRACE( "host window %p/%lx GravityNotify, rect %s\n", win, win->window, wine_dbgstr_rect(&win->rect) );
-        host_window_send_gravity_events( win, event->xany.display, event->xany.serial, previous );
+        host_window_send_gravity_events( win, event->xany.display, event->xany.serial );
         break;
     }
     case ConfigureNotify:
@@ -228,7 +227,7 @@ static BOOL host_window_filter_event( XEvent *event, XEvent *previous )
         SetRect( &win->rect, configure->x, configure->y, configure->x + configure->width, configure->y + configure->height );
         if (win->parent) win->rect = host_window_configure_child( win->parent, win->window, win->rect, configure->send_event );
         TRACE( "host window %p/%lx ConfigureNotify, rect %s\n", win, win->window, wine_dbgstr_rect(&win->rect) );
-        host_window_send_gravity_events( win, event->xany.display, event->xany.serial, previous );
+        host_window_send_gravity_events( win, event->xany.display, event->xany.serial );
         break;
     }
     }
@@ -322,114 +321,6 @@ static Bool filter_event( Display *display, XEvent *event, char *arg )
 }
 
 
-enum event_merge_action
-{
-    MERGE_DISCARD,  /* discard the old event */
-    MERGE_HANDLE,   /* handle the old event */
-    MERGE_KEEP,     /* keep the old event for future merging */
-    MERGE_IGNORE    /* ignore the new event, keep the old one */
-};
-
-/***********************************************************************
- *           merge_raw_motion_events
- */
-#ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
-static enum event_merge_action merge_raw_motion_events( XIRawEvent *prev, XIRawEvent *next )
-{
-    int i, j, k;
-    unsigned char mask;
-
-    if (!prev->valuators.mask_len) return MERGE_HANDLE;
-    if (!next->valuators.mask_len) return MERGE_HANDLE;
-
-    mask = prev->valuators.mask[0] | next->valuators.mask[0];
-    if (mask == next->valuators.mask[0])  /* keep next */
-    {
-        for (i = j = k = 0; i < 8; i++)
-        {
-            if (XIMaskIsSet( prev->valuators.mask, i ))
-                next->valuators.values[j] += prev->valuators.values[k++];
-            if (XIMaskIsSet( next->valuators.mask, i )) j++;
-        }
-        TRACE( "merging duplicate GenericEvent\n" );
-        return MERGE_DISCARD;
-    }
-    if (mask == prev->valuators.mask[0])  /* keep prev */
-    {
-        for (i = j = k = 0; i < 8; i++)
-        {
-            if (XIMaskIsSet( next->valuators.mask, i ))
-                prev->valuators.values[j] += next->valuators.values[k++];
-            if (XIMaskIsSet( prev->valuators.mask, i )) j++;
-        }
-        TRACE( "merging duplicate GenericEvent\n" );
-        return MERGE_IGNORE;
-    }
-    /* can't merge events with disjoint masks */
-    return MERGE_HANDLE;
-}
-#endif
-
-/***********************************************************************
- *           merge_events
- *
- * Try to merge 2 consecutive events.
- */
-static enum event_merge_action merge_events( XEvent *prev, XEvent *next )
-{
-    switch (prev->type)
-    {
-    case ConfigureNotify:
-        switch (next->type)
-        {
-        case ConfigureNotify:
-            if (prev->xany.window == next->xany.window)
-            {
-                TRACE( "discarding duplicate ConfigureNotify for window %lx\n", prev->xany.window );
-                return MERGE_DISCARD;
-            }
-            break;
-        case Expose:
-        case PropertyNotify:
-            return MERGE_KEEP;
-        }
-        break;
-    case MotionNotify:
-        switch (next->type)
-        {
-        case MotionNotify:
-            if (prev->xany.window == next->xany.window)
-            {
-                TRACE( "discarding duplicate MotionNotify for window %lx\n", prev->xany.window );
-                return MERGE_DISCARD;
-            }
-            break;
-#ifdef HAVE_X11_EXTENSIONS_XINPUT2_H
-        case GenericEvent:
-            if (next->xcookie.extension != xinput2_opcode) break;
-            if (next->xcookie.evtype != XI_RawMotion) break;
-            if (x11drv_thread_data()->warp_serial) break;
-            return MERGE_KEEP;
-        }
-        break;
-    case GenericEvent:
-        if (prev->xcookie.extension != xinput2_opcode) break;
-        if (prev->xcookie.evtype != XI_RawMotion) break;
-        switch (next->type)
-        {
-        case GenericEvent:
-            if (next->xcookie.extension != xinput2_opcode) break;
-            if (next->xcookie.evtype != XI_RawMotion) break;
-            if (x11drv_thread_data()->warp_serial) break;
-            return merge_raw_motion_events( prev->xcookie.data, next->xcookie.data );
-#endif
-        }
-        break;
-    }
-    return MERGE_HANDLE;
-}
-
-
 /***********************************************************************
  *           call_event_handler
  */
@@ -475,57 +366,25 @@ static int check_fd_events( int fd, int events )
  */
 BOOL X11DRV_ProcessEvents( DWORD mask )
 {
+    static const INPUT input = { .type = INPUT_MOUSE, .mi.dwFlags = MOUSEEVENTF_MOVE_NOCOALESCE };
     struct x11drv_thread_data *data = x11drv_thread_data();
-    XEvent event, prev_event;
-    int count = 0;
-    enum event_merge_action action = MERGE_DISCARD;
+    XEvent event;
+    int count;
 
     if (!data) return FALSE;
     if (data->current_event) mask = 0;  /* don't process nested events */
 
-    prev_event.type = 0;
-    for (;;)
+    for (count = 0; XCheckIfEvent( data->display, &event, filter_event, (XPointer)(UINT_PTR)mask ); count++)
     {
-        if (!XCheckIfEvent( data->display, &event, filter_event, (XPointer)(UINT_PTR)mask ))
-        {
-            if (!prev_event.type) break;
-            call_event_handler( data->display, &prev_event );
-            free_event_data( &prev_event );
-
-            /* Retry after processing delayed event, more events might have been read from the pipe,
-             * this is the case for instance with synchronous requests like when reading a property.
-             */
-            action = MERGE_DISCARD;
-            prev_event.type = 0;
-            continue;
-        }
-
-        count++;
-        if (XFilterEvent( &event, None )) continue;
-        if (host_window_filter_event( &event, &prev_event )) continue;
-
+        if (XFilterEvent( &event, None ) || host_window_filter_event( &event )) continue;
         get_event_data( &event );
-        if (prev_event.type) action = merge_events( &prev_event, &event );
-        switch( action )
-        {
-        case MERGE_HANDLE:  /* handle prev, keep new */
-            call_event_handler( data->display, &prev_event );
-            /* fall through */
-        case MERGE_DISCARD:  /* discard prev, keep new */
-            free_event_data( &prev_event );
-            prev_event = event;
-            break;
-        case MERGE_KEEP:  /* handle new, keep prev for future merging */
-            call_event_handler( data->display, &event );
-            /* fall through */
-        case MERGE_IGNORE: /* ignore new, keep prev for future merging */
-            free_event_data( &event );
-            break;
-        }
+        call_event_handler( data->display, &event );
+        free_event_data( &event );
     }
 
     XFlush( gdi_display );
     if (count) TRACE( "processed %d events\n", count );
+    NtUserSendHardwareInput( NULL, SEND_HWMSG_RAWINPUT, &input, 0 ); /* flush win32u accumulated motion */
 
     if (mask != QS_ALLINPUT || check_fd_events( ConnectionNumber( data->display ), POLLIN )) return FALSE;
     XFlush( data->display ); /* all events have been processed, flush any pending request */
@@ -858,6 +717,7 @@ static BOOL X11DRV_FocusIn( HWND hwnd, XEvent *xev )
     /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */
     if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE;
 
+    x11drv_xinput2_enable( event->display, DefaultRootWindow( event->display ) );
     xim_set_focus( hwnd, TRUE );
 
     if (use_take_focus) return TRUE;
@@ -951,6 +811,7 @@ static BOOL X11DRV_FocusOut( HWND hwnd, XEvent *xev )
     /* ignore wm specific NotifyUngrab / NotifyGrab events w.r.t focus */
     if (event->mode == NotifyGrab || event->mode == NotifyUngrab) return FALSE;
 
+    x11drv_xinput2_disable( event->display, DefaultRootWindow( event->display ) );
     focus_out( event->display, hwnd );
     return TRUE;
 }
@@ -1005,7 +866,7 @@ static BOOL X11DRV_Expose( HWND hwnd, XEvent *xev )
 
     release_win_data( data );
 
-    NtUserExposeWindowSurface( hwnd, flags, &rect, NtUserGetWinMonitorDpi( hwnd, MDT_RAW_DPI ) );
+    NtUserExposeWindowSurface( hwnd, flags, &rect );
     return TRUE;
 }
 
diff --git a/dlls/winex11.drv/init.c b/dlls/winex11.drv/init.c
index d5b4fdbf0aae..754349f68924 100644
--- a/dlls/winex11.drv/init.c
+++ b/dlls/winex11.drv/init.c
@@ -196,13 +196,6 @@ static HFONT X11DRV_SelectFont( PHYSDEV dev, HFONT hfont, UINT *aa_flags )
     return dev->funcs->pSelectFont( dev, hfont, aa_flags );
 }
 
-static BOOL get_surface_rect( HWND hwnd, RECT *rect, UINT dpi )
-{
-    if (!NtUserGetPresentRect( hwnd, rect, dpi ) && !NtUserGetClientRect( hwnd, rect, dpi )) return FALSE;
-    OffsetRect( rect, -rect->left, -rect->top );
-    return TRUE;
-}
-
 static BOOL needs_client_window_clipping( HWND hwnd )
 {
     RECT rect, client;
@@ -296,30 +289,14 @@ static void x11drv_client_surface_detach( struct client_surface *client )
 
 static void client_surface_update_geometry( HWND hwnd, struct x11drv_client_surface *surface )
 {
-    UINT dpi = NtUserGetDpiForWindow( hwnd ); /* use window DPI here, DPI scaling is handled through offscreen presentation */
-    HWND origin = hwnd, toplevel = NtUserGetAncestor( hwnd, GA_ROOT );
     XWindowChanges changes = surface->changes;
-    struct x11drv_win_data *data;
+    RECT rect = surface->client.virtual_rect;
     int mask = 0;
-    RECT rect;
-
-    if (NtUserGetPresentRect( hwnd, &rect, dpi )) OffsetRect( &rect, -rect.left, -rect.top );
-    else if (!NtUserGetClientRect( hwnd, &rect, dpi )) return;
-    else NtUserMapWindowPoints( origin, toplevel, (POINT *)&rect, 2, dpi );
-
-    if ((data = get_win_data( toplevel )))
-    {
-        OffsetRect( &rect, data->rects.client.left - data->rects.visible.left,
-                    data->rects.client.top - data->rects.visible.top );
-        release_win_data( data );
-    }
 
     changes.x = rect.left;
     changes.y = rect.top;
     changes.width  = min( max( 1, rect.right - rect.left ), 65535 );
     changes.height = min( max( 1, rect.bottom - rect.top ), 65535 );
-    OffsetRect( &rect, -rect.left, -rect.top );
-    surface->rect = rect;
 
     if (changes.x != surface->changes.x) mask |= CWX;
     if (changes.y != surface->changes.y) mask |= CWY;
@@ -370,9 +347,13 @@ static void client_surface_update_offscreen( HWND hwnd, struct x11drv_client_sur
     {
         static const WCHAR displayW[] = {'D','I','S','P','L','A','Y', 0};
         UNICODE_STRING device_str = RTL_CONSTANT_STRING(displayW);
+        RECT rect = surface->client.virtual_rect;
+
+        OffsetRect( &rect, -rect.left, -rect.top );
         surface->hdc_dst = NtGdiOpenDCW( &device_str, NULL, NULL, 0, TRUE, NULL, NULL, NULL );
         surface->hdc_src = NtGdiOpenDCW( &device_str, NULL, NULL, 0, TRUE, NULL, NULL, NULL );
-        set_dc_drawable( surface->hdc_src, surface->window, &surface->rect, IncludeInferiors );
+        set_dc_drawable( surface->hdc_src, surface->window, &rect, IncludeInferiors );
+
 #ifdef SONAME_LIBXCOMPOSITE
         if (usexcomposite) pXCompositeRedirectWindow( gdi_display, surface->window, CompositeRedirectManual );
 #endif
@@ -400,17 +381,13 @@ static void x11drv_client_surface_update( struct client_surface *client )
 static void X11DRV_client_surface_present( struct client_surface *client, HDC hdc )
 {
     struct x11drv_client_surface *surface = impl_from_client_surface( client );
-    HWND hwnd = client->hwnd, toplevel = NtUserGetAncestor( hwnd, GA_ROOT );
-    struct x11drv_win_data *data;
-    RECT rect_dst, rect;
+    HWND hwnd = client->hwnd, toplevel = client->toplevel;
+    RECT rect_dst, rect_src = client->virtual_rect, rect;
     Drawable window;
     HRGN region;
 
     TRACE( "%s\n", debugstr_client_surface( client ) );
 
-    client_surface_update_geometry( hwnd, surface );
-    client_surface_update_offscreen( hwnd, surface );
-
     if (!hdc) return;
     window = X11DRV_get_whole_window( toplevel );
 
@@ -423,15 +400,7 @@ static void X11DRV_client_surface_present( struct client_surface *client, HDC hd
     else
     {
         region = get_dc_monitor_region( hwnd, hdc ); /* otherwise use the window region for clipping rules */
-        if (!NtUserGetClientRect( hwnd, &rect_dst, NtUserGetWinMonitorDpi( hwnd, MDT_RAW_DPI ) )) goto done;
-        NtUserMapWindowPoints( hwnd, toplevel, (POINT *)&rect_dst, 2, NtUserGetWinMonitorDpi( hwnd, MDT_RAW_DPI ) );
-    }
-
-    if ((data = get_win_data( toplevel )))
-    {
-        OffsetRect( &rect_dst, data->rects.client.left - data->rects.visible.left,
-                    data->rects.client.top - data->rects.visible.top );
-        release_win_data( data );
+        rect_dst = client->monitor_rect;
     }
 
     if (get_dc_drawable( surface->hdc_dst, &rect ) != window || !EqualRect( &rect, &rect_dst ))
@@ -439,10 +408,9 @@ static void X11DRV_client_surface_present( struct client_surface *client, HDC hd
     if (region) NtGdiExtSelectClipRgn( surface->hdc_dst, region, RGN_COPY );
 
     NtGdiStretchBlt( surface->hdc_dst, 0, 0, rect_dst.right - rect_dst.left, rect_dst.bottom - rect_dst.top,
-                     surface->hdc_src, 0, 0, surface->rect.right, surface->rect.bottom, SRCCOPY, 0 );
+                     surface->hdc_src, 0, 0, rect_src.right - rect_src.left, rect_src.bottom - rect_src.top, SRCCOPY, 0 );
     XFlush( gdi_display );
 
-done:
     if (region) NtGdiDeleteObjectApp( region );
 }
 
@@ -479,9 +447,7 @@ struct client_surface *X11DRV_CreateClientSurface( HWND hwnd, int format )
 
     if (!(surface = client_surface_create( sizeof(*surface), &x11drv_client_surface_funcs, hwnd ))) goto failed;
     surface->colormap = colormap;
-
-    if (!get_surface_rect( hwnd, &surface->rect, NtUserGetDpiForWindow( hwnd ) )) goto failed;
-    if (!(surface->window = create_client_window( hwnd, surface->rect, &visual, colormap ))) goto failed;
+    if (!(surface->window = create_client_window( hwnd, surface->client.virtual_rect, &visual, colormap ))) goto failed;
 
     TRACE( "Created %s for client window %lx\n", debugstr_client_surface( &surface->client ), surface->window );
     return &surface->client;
diff --git a/dlls/winex11.drv/keyboard.c b/dlls/winex11.drv/keyboard.c
index 3d5d2c81f034..668a25b13455 100644
--- a/dlls/winex11.drv/keyboard.c
+++ b/dlls/winex11.drv/keyboard.c
@@ -57,6 +57,14 @@
 #include "wine/debug.h"
 #include "wine/list.h"
 
+#ifdef HAVE_LINUX_INPUT_H
+#include <linux/input.h>
+#elif defined HAVE_DEV_EVDEV_INPUT_H
+#include <dev/evdev/input.h>
+#else
+#define KEY_KPDOT 83
+#endif
+
 /* log format (add 0-padding as appropriate):
     keycode  %u  as in output from xev
     keysym   %lx as in X11/keysymdef.h
@@ -81,7 +89,151 @@ struct layout
 static const unsigned int ControlMask = 1 << 2;
 
 static int min_keycode, max_keycode, keysyms_per_keycode;
-static WORD keyc2vkey[256], keyc2scan[256];
+static WORD keyc2vkey[256], default_keyc2scan[0x100];
+static BOOL use_xkb;
+
+static void init_default_keyc2scan( Display *display )
+{
+    static const struct
+    {
+        WORD scancode;
+        KeySym keysym;
+    } keysym_map[] =
+    {
+        {0x01, XK_Escape}, {0x02, XK_1}, {0x03, XK_2}, {0x04, XK_3}, {0x05, XK_4}, {0x06, XK_5},
+        {0x07, XK_6}, {0x08, XK_7}, {0x09, XK_8}, {0x0a, XK_9}, {0x0b, XK_0}, {0x0c, XK_minus},
+        {0x0d, XK_equal}, {0x0e, XK_BackSpace}, {0x0f, XK_Tab}, {0x10, XK_q}, {0x11, XK_w},
+        {0x12, XK_e}, {0x13, XK_r}, {0x14, XK_t}, {0x15, XK_y}, {0x16, XK_u}, {0x17, XK_i},
+        {0x18, XK_o}, {0x19, XK_p}, {0x1a, XK_bracketleft}, {0x1b, XK_bracketright}, {0x1c, XK_Return},
+        {0x1d, XK_Control_L}, {0x1e, XK_a}, {0x1f, XK_s}, {0x20, XK_d}, {0x21, XK_f}, {0x22, XK_g},
+        {0x23, XK_h}, {0x24, XK_j}, {0x25, XK_k}, {0x26, XK_l}, {0x27, XK_semicolon},
+        {0x28, XK_apostrophe}, {0x29, XK_grave}, {0x2a, XK_Shift_L}, {0x2b, XK_backslash},
+        {0x2c, XK_z}, {0x2d, XK_x}, {0x2e, XK_c}, {0x2f, XK_v}, {0x30, XK_b}, {0x31, XK_n},
+        {0x32, XK_m}, {0x33, XK_comma}, {0x34, XK_period}, {0x35, XK_slash}, {0x36, XK_Shift_R},
+        {0x37, XK_asterisk}, {0x38, XK_Alt_L}, {0x38, XK_Meta_L}, {0x39, XK_space}, {0x3a, XK_Caps_Lock},
+        {0x3b, XK_F1}, {0x3c, XK_F2}, {0x3d, XK_F3}, {0x3e, XK_F4}, {0x3f, XK_F5}, {0x40, XK_F6},
+        {0x41, XK_F7}, {0x42, XK_F8}, {0x43, XK_F9}, {0x44, XK_F10}, {0x45, XK_Num_Lock},
+        {0x46, XK_Scroll_Lock}, {0x47, XK_KP_7}, {0x48, XK_KP_8}, {0x49, XK_KP_9},
+        {0x4a, XK_KP_Subtract}, {0x4b, XK_KP_4}, {0x4c, XK_KP_5}, {0x4d, XK_KP_6},
+        {0x4e, XK_KP_Add}, {0x4f, XK_KP_1}, {0x50, XK_KP_2}, {0x51, XK_KP_3},
+        {0x52, XK_KP_0}, {0x53, XK_KP_Decimal}, {0x57, XK_F11}, {0x58, XK_F12},
+
+        {0x11c, XK_KP_Enter}, {0x11d, XK_Control_R}, {0x135, XK_KP_Divide},
+        {0x138, XK_Alt_R}, {0x138, XK_Meta_R}, {0x138, XK_ISO_Level3_Shift},
+        {0x147, XK_Home}, {0x148, XK_Up}, {0x149, XK_Page_Up},
+        {0x14b, XK_Left}, {0x14d, XK_Right}, {0x14f, XK_End},
+        {0x150, XK_Down}, {0x151, XK_Page_Down}, {0x152, XK_Insert}, {0x153, XK_Delete},
+        {0x15b, XK_Super_L}, {0x15c, XK_Super_R}, {0x15d, XK_Menu},
+    };
+
+    for (unsigned int keyc = min_keycode; keyc <= max_keycode; keyc++)
+    {
+        XKeyEvent event = { .display = display, .keycode = keyc };
+        unsigned int i;
+        KeySym keysym;
+
+        if (!(keysym = XLookupKeysym( &event, 0 ))) continue;
+        for (i = 0; i < ARRAY_SIZE(keysym_map); i++) if (keysym_map[i].keysym == keysym) break;
+        if (i < ARRAY_SIZE(keysym_map)) default_keyc2scan[keyc - min_keycode] = keysym_map[i].scancode;
+        else WARN( "Failed to map keyc %#x keysym %#lx\n", keyc, keysym );
+    }
+}
+
+static WORD keyc2scan( unsigned int keycode, unsigned int state )
+{
+    unsigned int key = keycode - 8;
+
+    if (!use_xkb) return default_keyc2scan[keycode - min_keycode];
+
+    /* base keys can be mapped directly */
+    if (key <= KEY_KPDOT) return key;
+
+#ifdef KEY_SYSRQ
+    /* map keys found in KBDTABLES definitions (Txx Xxx Yxx macros) */
+    switch (key)
+    {
+    case 84 /* ISO_Level3_Shift */: return 0x005a; /* T5A / VK_OEM_WSCTRL */
+    case KEY_SYSRQ: return 0x0054; /* T54 / VK_SNAPSHOT */
+    case KEY_102ND: return 0x0056; /* T56 / VK_OEM_102 */
+    case KEY_F11: return 0x0057; /* T57 / VK_F11 */
+    case KEY_F12: return 0x0058; /* T58 / VK_F12 */
+    case KEY_LINEFEED: return 0x0059; /* T59 / VK_CLEAR */
+    case KEY_EXIT: return 0x005b; /* T5B / VK_OEM_FINISH */
+    case KEY_OPEN: return 0x005c; /* T5C / VK_OEM_JUMP */
+    /* FIXME: map a KEY to T5D / VK_EREOF */
+    /* FIXME: map a KEY to T5E / VK_OEM_BACKTAB */
+    case KEY_COMPOSE: return 0x005f; /* T5F / VK_OEM_AUTO */
+    case KEY_SCALE: return 0x0062; /* T62 / VK_ZOOM */
+    case KEY_HELP: return 0x0063; /* T63 / VK_HELP */
+    case KEY_F13: return 0x0064; /* T64 / VK_F13 */
+    case KEY_F14: return 0x0065; /* T65 / VK_F14 */
+    case KEY_F15: return 0x0066; /* T66 / VK_F15 */
+    case KEY_F16: return 0x0067; /* T67 / VK_F16 */
+    case KEY_F17: return 0x0068; /* T68 / VK_F17 */
+    case KEY_F18: return 0x0069; /* T69 / VK_F18 */
+    case KEY_F19: return 0x006a; /* T6A / VK_F19 */
+    case KEY_F20: return 0x006b; /* T6B / VK_F20 */
+    case KEY_F21: return 0x006c; /* T6C / VK_F21 */
+    case KEY_F22: return 0x006d; /* T6D / VK_F22 */
+    case KEY_F23: return 0x006e; /* T6E / VK_F23 */
+    /* FIXME: map a KEY to T6F / VK_OEM_PA3 */
+    case KEY_COMPUTER: return 0x0071; /* T71 / VK_OEM_RESET */
+    /* FIXME: map a KEY to T73 / VK_ABNT_C1 */
+    case KEY_F24: return 0x0076; /* T76 / VK_F24 */
+    case KEY_KPPLUSMINUS: return 0x007b; /* T7B / VK_OEM_PA1 */
+    /* FIXME: map a KEY to T7C / VK_TAB */
+    /* FIXME: map a KEY to T7E / VK_ABNT_C2 */
+    /* FIXME: map a KEY to T7F / VK_OEM_PA2 */
+    case KEY_PREVIOUSSONG: return 0x0110; /* X10 / VK_MEDIA_PREV_TRACK */
+    case KEY_NEXTSONG: return 0x0119; /* X19 / VK_MEDIA_NEXT_TRACK */
+    case KEY_KPENTER: return 0x011c; /* X1C / VK_RETURN */
+    case KEY_RIGHTCTRL: return 0x011d; /* X1D / VK_RCONTROL */
+    case KEY_MUTE: return 0x0120; /* X20 / VK_VOLUME_MUTE */
+    case KEY_PROG2: return 0x0121; /* X21 / VK_LAUNCH_APP2 */
+    case KEY_PLAYPAUSE: return 0x0122; /* X22 / VK_MEDIA_PLAY_PAUSE */
+    case KEY_STOPCD: return 0x0124; /* X24 / VK_MEDIA_STOP */
+    case KEY_VOLUMEDOWN: return 0x012e; /* X2E / VK_VOLUME_DOWN */
+    case KEY_VOLUMEUP: return 0x0130; /* X30 / VK_VOLUME_UP */
+    case KEY_HOMEPAGE: return 0x0132; /* X32 / VK_BROWSER_HOME */
+    case KEY_KPSLASH: return 0x0135; /* X35 / VK_DIVIDE */
+    case KEY_PRINT: return 0x0137; /* X37 / VK_SNAPSHOT */
+    case KEY_RIGHTALT: return 0x0138; /* X38 / VK_RMENU */
+    case KEY_CANCEL: return 0x0146; /* X46 / VK_CANCEL */
+    case KEY_HOME: return 0x0147; /* X47 / VK_HOME */
+    case KEY_UP: return 0x0148; /* X48 / VK_UP */
+    case KEY_PAGEUP: return 0x0149; /* X49 / VK_PRIOR */
+    case KEY_LEFT: return 0x014b; /* X4B / VK_LEFT */
+    case KEY_RIGHT: return 0x014d; /* X4D / VK_RIGHT */
+    case KEY_END: return 0x014f; /* X4F / VK_END */
+    case KEY_DOWN: return 0x0150; /* X50 / VK_DOWN */
+    case KEY_PAGEDOWN: return 0x0151; /* X51 / VK_NEXT */
+    case KEY_INSERT: return 0x0152; /* X52 / VK_INSERT */
+    case KEY_DELETE: return 0x0153; /* X53 / VK_DELETE */
+    case KEY_LEFTMETA: return 0x015b; /* X5B / VK_LWIN */
+    case KEY_RIGHTMETA: return 0x015c; /* X5C / VK_RWIN */
+    case KEY_MENU: return 0x015d; /* X5D / VK_APPS */
+    case KEY_POWER: return 0x015e; /* X5E / VK_POWER */
+    case KEY_SLEEP: return 0x015f; /* X5F / VK_SLEEP */
+    case KEY_FIND: return 0x0165; /* X65 / VK_BROWSER_SEARCH */
+    case KEY_BOOKMARKS: return 0x0166; /* X66 / VK_BROWSER_FAVORITES */
+    case KEY_REFRESH: return 0x0167; /* X67 / VK_BROWSER_REFRESH */
+    case KEY_STOP: return 0x0168; /* X68 / VK_BROWSER_STOP */
+    case KEY_FORWARD: return 0x0169; /* X69 / VK_BROWSER_FORWARD */
+    case KEY_BACK: return 0x016a; /* X6A / VK_BROWSER_BACK */
+    case KEY_PROG1: return 0x016b; /* X6B / VK_LAUNCH_APP1 */
+    case KEY_MAIL: return 0x016c; /* X6C / VK_LAUNCH_MAIL */
+    case KEY_MEDIA: return 0x016d; /* X6D / VK_LAUNCH_MEDIA_SELECT */
+    case KEY_PAUSE:
+        if (state & ControlMask) return 0x0146; /* X46 / VK_CANCEL */
+        return 0x021d; /* Y1D / VK_PAUSE */
+    }
+#else
+    FIXME( "evdev keycodes not available, scancode mapping is going to be broken\n" );
+#endif
+
+    /* otherwise just make up some extended scancode */
+    return 0x200 | (key & 0x7f);
+}
 
 static int NumLockMask, ScrollLockMask, AltGrMask; /* mask in the XKeyEvent state */
 
@@ -179,46 +331,6 @@ static void create_layout_from_xkb( int xkb_group, const char *xkb_layout, LANGI
 
 /* Keyboard translation tables */
 #define MAIN_LEN 49
-static const WORD main_key_scan_qwerty[MAIN_LEN] =
-{
-/* this is my (102-key) keyboard layout, sorry if it doesn't quite match yours */
- /* `    1    2    3    4    5    6    7    8    9    0    -    = */
-   0x29,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,
- /* q    w    e    r    t    y    u    i    o    p    [    ] */
-   0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,
- /* a    s    d    f    g    h    j    k    l    ;    '    \ */
-   0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x2B,
- /* z    x    c    v    b    n    m    ,    .    / */
-   0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,
-   0x56 /* the 102nd key (actually to the right of l-shift) */
-};
-
-static const WORD main_key_scan_abnt_qwerty[MAIN_LEN] =
-{
- /* `    1    2    3    4    5    6    7    8    9    0    -    = */
-   0x29,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,
- /* q    w    e    r    t    y    u    i    o    p    [    ] */
-   0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,
- /* a    s    d    f    g    h    j    k    l    ;    '    \ */
-   0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x2B,
- /* \      z    x    c    v    b    n    m    ,    .    / */
-   0x5e,0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,
-   0x56, /* the 102nd key (actually to the right of l-shift) */
-};
-
-static const WORD main_key_scan_qwerty_jp106[MAIN_LEN] =
-{
- /* 1    2    3    4    5    6    7    8    9    0    -    ^    \ (Yen) */
-   0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x7D,
- /* q    w    e    r    t    y    u    i    o    p    @    [ */
-   0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1A,0x1B,
- /* a    s    d    f    g    h    j    k    l    ;    :    ] */
-   0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28,0x2B,
- /* z    x    c    v    b    n    m    ,    .    /    \ (Underscore) */
-   0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32,0x33,0x34,0x35,0x73
-};
-
-
 static const WORD main_key_vkey_qwerty[MAIN_LEN] =
 {
 /* NOTE: this layout must concur with the scan codes layout above */
@@ -936,13 +1048,6 @@ static const char main_key_th[MAIN_LEN][4] =
 }; 
 
 /*** VNC keyboard layout */
-static const WORD main_key_scan_vnc[MAIN_LEN] =
-{
-   0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x1A,0x1B,0x27,0x28,0x29,0x33,0x34,0x35,0x2B,
-   0x1E,0x30,0x2E,0x20,0x12,0x21,0x22,0x23,0x17,0x24,0x25,0x26,0x32,0x31,0x18,0x19,0x10,0x13,0x1F,0x14,0x16,0x2F,0x11,0x2D,0x15,0x2C,
-   0x56
-};
-
 static const WORD main_key_vkey_vnc[MAIN_LEN] =
 {
    '1','2','3','4','5','6','7','8','9','0',VK_OEM_MINUS,VK_OEM_PLUS,VK_OEM_4,VK_OEM_6,VK_OEM_1,VK_OEM_7,VK_OEM_3,VK_OEM_COMMA,VK_OEM_PERIOD,VK_OEM_2,VK_OEM_5,
@@ -974,76 +1079,74 @@ static const struct {
                  in the appropriate dlls/kernel/nls/.nls file */
     const char *comment;
     const char (*key)[MAIN_LEN][4];
-    const WORD (*scan)[MAIN_LEN]; /* scan codes mapping */
     const WORD (*vkey)[MAIN_LEN]; /* virtual key codes mapping */
 } main_key_tab[]={
- {0x0409, "United States keyboard layout", &main_key_US, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0409, "United States keyboard layout (phantom key version)", &main_key_US_phantom, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- /* Dvorak users tend to run QWERTY keyboards and rely on Windows/X11/Wayland to translate to the correct keysyms */
- {0x0409, "United States keyboard layout (dvorak)", &main_key_US_dvorak, &main_key_scan_qwerty, &main_key_vkey_dvorak},
- {0x0409, "United States keyboard layout (programmer dvorak)", &main_key_US_programmer_dvorak, &main_key_scan_qwerty, &main_key_vkey_dvorak},
- {0x0409, "United States keyboard layout (dvorak with phantom key)", &main_key_US_dvorak_phantom, &main_key_scan_qwerty, &main_key_vkey_dvorak},
- {0x0409, "United States International keyboard layout", &main_key_US_intl, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0809, "British keyboard layout", &main_key_UK, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0407, "German keyboard layout", &main_key_DE, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x0807, "Swiss German keyboard layout", &main_key_SG, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x100c, "Swiss French keyboard layout", &main_key_SF, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x041d, "Swedish keyboard layout", &main_key_SE, &main_key_scan_qwerty, &main_key_vkey_qwerty_v2},
- {0x0425, "Estonian keyboard layout", &main_key_ET, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0414, "Norwegian keyboard layout", &main_key_NO, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0406, "Danish keyboard layout", &main_key_DA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040c, "French keyboard layout", &main_key_FR, &main_key_scan_qwerty, &main_key_vkey_azerty},
- {0x0c0c, "Canadian French keyboard layout", &main_key_CF, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0c0c, "Canadian French keyboard layout (CA_fr)", &main_key_CA_fr, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0c0c, "Canadian keyboard layout", &main_key_CA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x080c, "Belgian keyboard layout", &main_key_BE, &main_key_scan_qwerty, &main_key_vkey_azerty},
- {0x0816, "Portuguese keyboard layout", &main_key_PT, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0416, "Brazilian ABNT-2 keyboard layout", &main_key_PT_br, &main_key_scan_abnt_qwerty, &main_key_vkey_abnt_qwerty},
- {0x0416, "Brazilian ABNT-2 keyboard layout ALT GR", &main_key_PT_br_alt_gr,&main_key_scan_abnt_qwerty, &main_key_vkey_abnt_qwerty},
- {0x040b, "Finnish keyboard layout", &main_key_FI, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0402, "Bulgarian bds keyboard layout", &main_key_BG_bds, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0402, "Bulgarian phonetic keyboard layout", &main_key_BG_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0423, "Belarusian keyboard layout", &main_key_BY, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0419, "Russian keyboard layout", &main_key_RU, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0419, "Russian keyboard layout (phantom key version)", &main_key_RU_phantom, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0419, "Russian keyboard layout KOI8-R", &main_key_RU_koi8r, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0419, "Russian keyboard layout cp1251", &main_key_RU_cp1251, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0419, "Russian phonetic keyboard layout", &main_key_RU_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0422, "Ukrainian keyboard layout KOI8-U", &main_key_UA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0422, "Ukrainian keyboard layout (standard)", &main_key_UA_std, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0419, "Russian keyboard layout (standard)", &main_key_RU_std, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040a, "Spanish keyboard layout", &main_key_ES, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0410, "Italian keyboard layout", &main_key_IT, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040f, "Icelandic keyboard layout", &main_key_IS, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040e, "Hungarian keyboard layout", &main_key_HU, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x0415, "Polish (programmer's) keyboard layout", &main_key_PL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0424, "Slovenian keyboard layout", &main_key_SI, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x0c1a, "Serbian keyboard layout sr", &main_key_SR, &main_key_scan_qwerty, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */
- {0x0c1a, "Serbian keyboard layout us,sr", &main_key_US_SR, &main_key_scan_qwerty, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */
- {0x041a, "Croatian keyboard layout", &main_key_HR, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x041a, "Croatian keyboard layout (specific)", &main_key_HR_jelly, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0411, "Japanese 106 keyboard layout", &main_key_JA_jp106, &main_key_scan_qwerty_jp106, &main_key_vkey_qwerty_jp106},
- {0x0411, "Japanese Mac keyboard layout", &main_key_JA_macjp, &main_key_scan_qwerty_jp106, &main_key_vkey_qwerty_jp106},
- {0x0411, "Japanese pc98x1 keyboard layout", &main_key_JA_pc98x1, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041b, "Slovak keyboard layout", &main_key_SK, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041b, "Slovak and Czech keyboard layout without dead keys", &main_key_SK_prog, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0405, "Czech keyboard layout", &main_key_CS, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0405, "Czech keyboard layout cz", &main_key_CZ, &main_key_scan_qwerty, &main_key_vkey_qwertz},
- {0x0405, "Czech keyboard layout cz_qwerty", &main_key_CZ_qwerty, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040a, "Latin American keyboard layout", &main_key_LA, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0427, "Lithuanian (Baltic) keyboard layout", &main_key_LT_B, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041f, "Turkish keyboard layout", &main_key_TK, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041f, "Turkish keyboard layout tr", &main_key_TR, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041f, "Turkish keyboard layout trf", &main_key_TR_F, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040d, "Israelian keyboard layout", &main_key_IL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040d, "Israelian phonetic keyboard layout", &main_key_IL_phonetic, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x040d, "Israelian Saharon keyboard layout", &main_key_IL_saharon, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0409, "VNC keyboard layout", &main_key_vnc, &main_key_scan_vnc, &main_key_vkey_vnc},
- {0x0408, "Greek keyboard layout", &main_key_EL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x041e, "Thai (Kedmanee)  keyboard layout", &main_key_th, &main_key_scan_qwerty, &main_key_vkey_qwerty},
- {0x0413, "Dutch keyboard layout", &main_key_NL, &main_key_scan_qwerty, &main_key_vkey_qwerty},
-
- {0, NULL, NULL, NULL, NULL} /* sentinel */
+ {0x0409, "United States keyboard layout", &main_key_US, &main_key_vkey_qwerty},
+ {0x0409, "United States keyboard layout (phantom key version)", &main_key_US_phantom, &main_key_vkey_qwerty},
+ {0x0409, "United States keyboard layout (dvorak)", &main_key_US_dvorak, &main_key_vkey_dvorak},
+ {0x0409, "United States keyboard layout (programmer dvorak)", &main_key_US_programmer_dvorak, &main_key_vkey_dvorak},
+ {0x0409, "United States keyboard layout (dvorak with phantom key)", &main_key_US_dvorak_phantom, &main_key_vkey_dvorak},
+ {0x0409, "United States International keyboard layout", &main_key_US_intl, &main_key_vkey_qwerty},
+ {0x0809, "British keyboard layout", &main_key_UK, &main_key_vkey_qwerty},
+ {0x0407, "German keyboard layout", &main_key_DE, &main_key_vkey_qwertz},
+ {0x0807, "Swiss German keyboard layout", &main_key_SG, &main_key_vkey_qwertz},
+ {0x100c, "Swiss French keyboard layout", &main_key_SF, &main_key_vkey_qwertz},
+ {0x041d, "Swedish keyboard layout", &main_key_SE, &main_key_vkey_qwerty_v2},
+ {0x0425, "Estonian keyboard layout", &main_key_ET, &main_key_vkey_qwerty},
+ {0x0414, "Norwegian keyboard layout", &main_key_NO, &main_key_vkey_qwerty},
+ {0x0406, "Danish keyboard layout", &main_key_DA, &main_key_vkey_qwerty},
+ {0x040c, "French keyboard layout", &main_key_FR, &main_key_vkey_azerty},
+ {0x0c0c, "Canadian French keyboard layout", &main_key_CF, &main_key_vkey_qwerty},
+ {0x0c0c, "Canadian French keyboard layout (CA_fr)", &main_key_CA_fr, &main_key_vkey_qwerty},
+ {0x0c0c, "Canadian keyboard layout", &main_key_CA, &main_key_vkey_qwerty},
+ {0x080c, "Belgian keyboard layout", &main_key_BE, &main_key_vkey_azerty},
+ {0x0816, "Portuguese keyboard layout", &main_key_PT, &main_key_vkey_qwerty},
+ {0x0416, "Brazilian ABNT-2 keyboard layout", &main_key_PT_br, &main_key_vkey_abnt_qwerty},
+ {0x0416, "Brazilian ABNT-2 keyboard layout ALT GR", &main_key_PT_br_alt_gr,&main_key_vkey_abnt_qwerty},
+ {0x040b, "Finnish keyboard layout", &main_key_FI, &main_key_vkey_qwerty},
+ {0x0402, "Bulgarian bds keyboard layout", &main_key_BG_bds, &main_key_vkey_qwerty},
+ {0x0402, "Bulgarian phonetic keyboard layout", &main_key_BG_phonetic, &main_key_vkey_qwerty},
+ {0x0423, "Belarusian keyboard layout", &main_key_BY, &main_key_vkey_qwerty},
+ {0x0419, "Russian keyboard layout", &main_key_RU, &main_key_vkey_qwerty},
+ {0x0419, "Russian keyboard layout (phantom key version)", &main_key_RU_phantom, &main_key_vkey_qwerty},
+ {0x0419, "Russian keyboard layout KOI8-R", &main_key_RU_koi8r, &main_key_vkey_qwerty},
+ {0x0419, "Russian keyboard layout cp1251", &main_key_RU_cp1251, &main_key_vkey_qwerty},
+ {0x0419, "Russian phonetic keyboard layout", &main_key_RU_phonetic, &main_key_vkey_qwerty},
+ {0x0422, "Ukrainian keyboard layout KOI8-U", &main_key_UA, &main_key_vkey_qwerty},
+ {0x0422, "Ukrainian keyboard layout (standard)", &main_key_UA_std, &main_key_vkey_qwerty},
+ {0x0419, "Russian keyboard layout (standard)", &main_key_RU_std, &main_key_vkey_qwerty},
+ {0x040a, "Spanish keyboard layout", &main_key_ES, &main_key_vkey_qwerty},
+ {0x0410, "Italian keyboard layout", &main_key_IT, &main_key_vkey_qwerty},
+ {0x040f, "Icelandic keyboard layout", &main_key_IS, &main_key_vkey_qwerty},
+ {0x040e, "Hungarian keyboard layout", &main_key_HU, &main_key_vkey_qwertz},
+ {0x0415, "Polish (programmer's) keyboard layout", &main_key_PL, &main_key_vkey_qwerty},
+ {0x0424, "Slovenian keyboard layout", &main_key_SI, &main_key_vkey_qwertz},
+ {0x0c1a, "Serbian keyboard layout sr", &main_key_SR, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */
+ {0x0c1a, "Serbian keyboard layout us,sr", &main_key_US_SR, &main_key_vkey_qwerty}, /* LANG_SERBIAN,SUBLANG_SERBIAN_CYRILLIC */
+ {0x041a, "Croatian keyboard layout", &main_key_HR, &main_key_vkey_qwertz},
+ {0x041a, "Croatian keyboard layout (specific)", &main_key_HR_jelly, &main_key_vkey_qwerty},
+ {0x0411, "Japanese 106 keyboard layout", &main_key_JA_jp106, &main_key_vkey_qwerty_jp106},
+ {0x0411, "Japanese Mac keyboard layout", &main_key_JA_macjp, &main_key_vkey_qwerty_jp106},
+ {0x0411, "Japanese pc98x1 keyboard layout", &main_key_JA_pc98x1, &main_key_vkey_qwerty},
+ {0x041b, "Slovak keyboard layout", &main_key_SK, &main_key_vkey_qwerty},
+ {0x041b, "Slovak and Czech keyboard layout without dead keys", &main_key_SK_prog, &main_key_vkey_qwerty},
+ {0x0405, "Czech keyboard layout", &main_key_CS, &main_key_vkey_qwerty},
+ {0x0405, "Czech keyboard layout cz", &main_key_CZ, &main_key_vkey_qwertz},
+ {0x0405, "Czech keyboard layout cz_qwerty", &main_key_CZ_qwerty, &main_key_vkey_qwerty},
+ {0x040a, "Latin American keyboard layout", &main_key_LA, &main_key_vkey_qwerty},
+ {0x0427, "Lithuanian (Baltic) keyboard layout", &main_key_LT_B, &main_key_vkey_qwerty},
+ {0x041f, "Turkish keyboard layout", &main_key_TK, &main_key_vkey_qwerty},
+ {0x041f, "Turkish keyboard layout tr", &main_key_TR, &main_key_vkey_qwerty},
+ {0x041f, "Turkish keyboard layout trf", &main_key_TR_F, &main_key_vkey_qwerty},
+ {0x040d, "Israelian keyboard layout", &main_key_IL, &main_key_vkey_qwerty},
+ {0x040d, "Israelian phonetic keyboard layout", &main_key_IL_phonetic, &main_key_vkey_qwerty},
+ {0x040d, "Israelian Saharon keyboard layout", &main_key_IL_saharon, &main_key_vkey_qwerty},
+ {0x0409, "VNC keyboard layout", &main_key_vnc, &main_key_vkey_vnc},
+ {0x0408, "Greek keyboard layout", &main_key_EL, &main_key_vkey_qwerty},
+ {0x041e, "Thai (Kedmanee)  keyboard layout", &main_key_th, &main_key_vkey_qwerty},
+ {0x0413, "Dutch keyboard layout", &main_key_NL, &main_key_vkey_qwerty},
+
+ {0, NULL, NULL, NULL} /* sentinel */
 };
 static unsigned kbd_layout=0; /* index into above table of layouts */
 #ifdef SONAME_LIBXKBREGISTRY
@@ -1127,53 +1230,6 @@ static const WORD nonchar_key_vkey[256] =
     0, 0, 0, 0, 0, 0, 0, VK_DELETE                              /* FFF8 */
 };
 
-static const WORD nonchar_key_scan[256] =
-{
-    /* unused */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF00 */
-    /* special keys */
-    0x0E, 0x0F, 0x00, /*?*/ 0, 0x00, 0x1C, 0x00, 0x00,           /* FF08 */
-    0x00, 0x00, 0x00, 0x45, 0x46, 0x00, 0x00, 0x00,              /* FF10 */
-    0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,              /* FF18 */
-    /* Japanese special keys */
-    0x00, 0x29, 0x7B, 0x79, 0x70, 0x00, 0x00, 0x70,              /* FF20 */
-    0x00, 0x00, 0x29, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF28 */
-    /* Korean special keys (FF31-) */
-    0x3A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF30 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF38 */
-    /* unused */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF40 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF48 */
-    /* cursor keys */
-    0x147, 0x14B, 0x148, 0x14D, 0x150, 0x149, 0x151, 0x14F,      /* FF50 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF58 */
-    /* misc keys */
-    /*?*/ 0, 0x137, /*?*/ 0, 0x152, 0x00, 0x00, 0x00, 0x15D,     /* FF60 */
-    /*?*/ 0, /*?*/ 0, 0x38, 0x146, 0x00, 0x00, 0x00, 0x00,       /* FF68 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF70 */
-    /* keypad keys */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x145,             /* FF78 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FF80 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x11C, 0x00, 0x00,             /* FF88 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x4B, 0x48,              /* FF90 */
-    0x4D, 0x50, 0x49, 0x51, 0x4F, 0x4C, 0x52, 0x53,              /* FF98 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFA0 */
-    0x00, 0x00, 0x37, 0x4E, 0x53, 0x4A, 0x53, 0x135,             /* FFA8 */
-    0x52, 0x4F, 0x50, 0x51, 0x4B, 0x4C, 0x4D, 0x47,              /* FFB0 */
-    0x48, 0x49, 0x00, 0x00, 0x00, 0x00,                          /* FFB8 */
-    /* function keys */
-    0x3B, 0x3C,
-    0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44,              /* FFC0 */
-    0x57, 0x58, 0x5B, 0x5C, 0x5D, 0x00, 0x00, 0x00,              /* FFC8 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFD0 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFD8 */
-    /* modifier keys */
-    0x00, 0x2A, 0x136, 0x1D, 0x11D, 0x3A, 0x00, 0x38,            /* FFE0 */
-    0x138, 0x38, 0x138, 0x15b, 0x15c, 0x00, 0x00, 0x00,          /* FFE8 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,              /* FFF0 */
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x153              /* FFF8 */
-};
-
 static const WORD xfree86_vendor_key_vkey[256] =
 {
     0, 0, 0, 0, 0, 0, 0, 0,                                     /* 1008FF00 */
@@ -1339,7 +1395,7 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event )
             if (!keys[vkey & 0xff].vkey)
             {
                 keys[vkey & 0xff].vkey = vkey;
-                keys[vkey & 0xff].scan = keyc2scan[keycode] & 0xff;
+                keys[vkey & 0xff].scan = keyc2scan( keycode, 0 );
             }
 
             if (event->xkeymap.key_vector[i] & (1<<j)) keys[vkey & 0xff].pressed = TRUE;
@@ -1374,8 +1430,8 @@ BOOL X11DRV_KeymapNotify( HWND hwnd, XEvent *event )
             {
                 TRACE( "Sending KEYUP for a modifier %#.2x\n", vkey);
                 flags = KEYEVENTF_KEYUP;
-                if (keys[vkey].vkey & 0x1000) flags |= KEYEVENTF_EXTENDEDKEY;
-                X11DRV_send_keyboard_input( keymapnotify_hwnd, vkey, keys[vkey].scan, flags, NtGetTickCount() );
+                if (keys[vkey].scan & 0x100) flags |= KEYEVENTF_EXTENDEDKEY;
+                X11DRV_send_keyboard_input( keymapnotify_hwnd, vkey, keys[vkey].scan & 0xff, flags, NtGetTickCount() );
             }
 
             update_key_state( keystate, vkey, keys[vkey].pressed );
@@ -1461,7 +1517,7 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
     char buf[24];
     char *Str = buf;
     KeySym keysym = 0;
-    WORD vkey = 0, bScan;
+    WORD vkey = 0, scan;
     DWORD dwFlags;
     int ascii_chars;
     XIC xic = X11DRV_get_ic( hwnd );
@@ -1525,13 +1581,13 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
     if (buf != Str)
         free( Str );
 
+    scan = keyc2scan( event->keycode, event->state );
     vkey = EVENT_event_to_vkey(xic,event);
     /* X returns keycode 0 for composed characters */
     if (!vkey && ascii_chars) vkey = VK_NONAME;
-    bScan = keyc2scan[event->keycode] & 0xFF;
 
-    TRACE_(key)("keycode %u converted to vkey 0x%X scan %02x\n",
-                event->keycode, vkey, bScan);
+    TRACE_(key)("keycode %u converted to vkey 0x%X scan %04x\n",
+                event->keycode, vkey, scan);
 
     pthread_mutex_unlock( &kbd_mutex );
 
@@ -1539,11 +1595,11 @@ BOOL X11DRV_KeyEvent( HWND hwnd, XEvent *xev )
 
     dwFlags = 0;
     if ( event->type == KeyRelease ) dwFlags |= KEYEVENTF_KEYUP;
-    if ( vkey & 0x100 )              dwFlags |= KEYEVENTF_EXTENDEDKEY;
+    if ( scan & 0x100 ) dwFlags |= KEYEVENTF_EXTENDEDKEY;
 
     update_lock_state( hwnd, vkey, event->state, event_time );
 
-    X11DRV_send_keyboard_input( hwnd, vkey & 0xff, bScan, dwFlags, event_time );
+    X11DRV_send_keyboard_input( hwnd, vkey & 0xff, scan & 0xff, dwFlags, event_time );
     return TRUE;
 }
 
@@ -1838,12 +1894,12 @@ static unsigned int detect_keyboard_layout( Display *display, XModifierKeymap *m
 }
 
 
-/* initialize keyc2scan and keyc2vkey */
+/* initialize keyc2vkey */
 static void init_keycode_mappings( Display *display )
 {
     KeySym keysym;
     XKeyEvent e2;
-    WORD scan, vkey;
+    WORD vkey;
     int keyc, i, keyn, syms;
     char ckey[4]={0,0,0,0};
     const char (*lkey)[MAIN_LEN][4];
@@ -1888,23 +1944,18 @@ static void init_keycode_mappings( Display *display )
         keysym = 0;
         e2.keycode = (KeyCode)keyc;
         have_chars = XLookupString(&e2, buf, sizeof(buf), &keysym, NULL);
-        vkey = 0; scan = 0;
+        vkey = 0;
         if (keysym)  /* otherwise, keycode not used */
         {
             if ((keysym >> 8) == 0xFF)         /* non-character key */
             {
                 vkey = nonchar_key_vkey[keysym & 0xff];
-                scan = nonchar_key_scan[keysym & 0xff];
-		/* set extended bit when necessary */
-		if (scan & 0x100) vkey |= 0x100;
             } else if ((keysym >> 8) == 0x1008FF) { /* XFree86 vendor keys */
                 vkey = xfree86_vendor_key_vkey[keysym & 0xff];
                 /* All vendor keys are extended with a scan code of 0 per testing on WinXP */
-                scan = 0x100;
 		vkey |= 0x100;
             } else if (keysym == 0x20) {                 /* Spacebar */
 	        vkey = VK_SPACE;
-		scan = 0x39;
 	    } else if (have_chars) {
 	      /* we seem to need to search the layout-dependent scancodes */
 	      int maxlen=0,maxval=-1,ok;
@@ -1936,16 +1987,13 @@ static void init_keycode_mappings( Display *display )
 	      }
 	      if (maxval>=0) {
 		/* got it */
-		const WORD (*lscan)[MAIN_LEN] = main_key_tab[kbd_layout].scan;
 		const WORD (*lvkey)[MAIN_LEN] = main_key_tab[kbd_layout].vkey;
-		scan = (*lscan)[maxval];
 		vkey = (*lvkey)[maxval];
 	      }
 	    }
         }
         TRACE("keycode %u => vkey %04X\n", e2.keycode, vkey);
         keyc2vkey[e2.keycode] = vkey;
-        keyc2scan[e2.keycode] = scan;
         if ((vkey & 0xff) && vkey_used[(vkey & 0xff)])
             WARN("vkey %04X is being used by more than one keycode\n", vkey);
         vkey_used[(vkey & 0xff)] = 1;
@@ -2054,20 +2102,6 @@ static void init_keycode_mappings( Display *display )
         vkey_used[vkey] = 1;
     } /* for */
 #undef VKEY_IF_NOT_USED
-
-    /* If some keys still lack scancodes, assign some arbitrary ones to them now */
-    for (scan = 0x60, keyc = min_keycode; keyc <= max_keycode; keyc++)
-      if (keyc2vkey[keyc]&&!keyc2scan[keyc]) {
-	const char *ksname;
-	keysym = XkbKeycodeToKeysym( display, keyc, 0, 0 );
-	ksname = XKeysymToString(keysym);
-	if (!ksname) ksname = "NoSymbol";
-
-	/* should make sure the scancode is unassigned here, but >=0x60 currently always is */
-
-	TRACE_(key)("assigning scancode %02x to unidentified keycode %u (%s)\n",scan,keyc,ksname);
-	keyc2scan[keyc]=scan++;
-      }
 }
 
 static void find_xkb_layout_variant( Display *display, XModifierKeymap *mmp, int group, const char *name,
@@ -2148,6 +2182,8 @@ void init_keyboard_layouts( Display *display )
     LIST_FOR_EACH_ENTRY( entry, &xkb_layouts, struct layout, entry )
         entry->xkb_group = -1;
 
+    if (!use_xkb) init_default_keyc2scan( display );
+
     status = XkbGetState( display, XkbUseCoreKbd, &xkb_state );
     xkb_group = status ? 0 : xkb_state.group;
     TRACE( "current group %u (status %#x)\n", xkb_group, status );
@@ -2239,7 +2275,8 @@ BOOL X11DRV_MappingNotify( HWND dummy, XEvent *event )
  */
 void x11drv_init_keyboard( Display *display )
 {
-    XkbUseExtension( display, NULL, NULL );
+    use_xkb = XkbUseExtension( display, NULL, NULL );
+    TRACE( "XkbUseExtension returned %#x\n", use_xkb );
 
 #ifdef SONAME_LIBXKBREGISTRY
     if (!(xkbregistry_handle = dlopen( SONAME_LIBXKBREGISTRY, RTLD_NOW )))
@@ -2388,7 +2425,7 @@ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl )
             {
                 if ((keyc2vkey[keyc] & 0xFF) == wCode)
                 {
-                    ret = keyc2scan[keyc] & 0xFF;
+                    ret = keyc2scan( keyc, 0 ) & 0xFF;
                     break;
                 }
             }
@@ -2404,7 +2441,7 @@ UINT X11DRV_MapVirtualKeyEx( UINT wCode, UINT wMapType, HKL hkl )
 
             /* let's do scan -> keycode -> vkey */
             for (keyc = min_keycode; keyc <= max_keycode; keyc++)
-                if ((keyc2scan[keyc] & 0xFF) == (wCode & 0xFF))
+                if ((keyc2scan( keyc, 0 ) & 0xFF) == (wCode & 0xFF))
                 {
                     ret = keyc2vkey[keyc] & 0xFF;
                     /* Only stop if it's not a numpad vkey; otherwise keep
@@ -2515,6 +2552,7 @@ INT X11DRV_GetKeyNameText( LONG lParam, LPWSTR lpBuffer, INT nSize )
 
   scanCode = lParam >> 16;
   scanCode &= 0x1ff;  /* keep "extended-key" flag with code */
+  if (scanCode == 0x145) return -1; /* use default implementation for VK_PAUSE */
 
   vkey = X11DRV_MapVirtualKeyEx( scanCode, MAPVK_VSC_TO_VK_EX, NtUserGetKeyboardLayout(0) );
 
@@ -2576,7 +2614,7 @@ INT X11DRV_GetKeyNameText( LONG lParam, LPWSTR lpBuffer, INT nSize )
   pthread_mutex_lock( &kbd_mutex );
 
   for (keyi=min_keycode; keyi<=max_keycode; keyi++)
-      if ((keyc2scan[keyi]) == scanCode)
+      if (keyc2scan( keyi, 0 ) == scanCode)
          break;
   if (keyi <= max_keycode)
   {
diff --git a/dlls/winex11.drv/mouse.c b/dlls/winex11.drv/mouse.c
index 5b36def3f17c..7e4d6f9b0312 100644
--- a/dlls/winex11.drv/mouse.c
+++ b/dlls/winex11.drv/mouse.c
@@ -267,6 +267,7 @@ void x11drv_xinput2_enable( Display *display, Window window )
 
     if (window == DefaultRootWindow( display ))
     {
+        if (x11drv_thread_data()->root_window_users++) return;
         XISetMask( mask_bits, XI_DeviceChanged );
         XISetMask( mask_bits, XI_RawMotion );
         XISetMask( mask_bits, XI_ButtonPress );
@@ -298,7 +299,10 @@ void x11drv_xinput2_disable( Display *display, Window window )
     memset( mask_bits, 0, sizeof(mask_bits) );
 
     if (window == DefaultRootWindow( display ))
+    {
+        if (--x11drv_thread_data()->root_window_users) return;
         XISetMask( mask_bits, XI_DeviceChanged );
+    }
 
     pXISelectEvents( display, window, &mask, 1 );
 }
@@ -389,9 +393,6 @@ static BOOL grab_clipping_window( const RECT *clip )
         return TRUE;
     }
 
-    /* enable XInput2 unless we are already clipping */
-    if (!data->clipping_cursor) x11drv_xinput2_enable( data->display, DefaultRootWindow( data->display ) );
-
     TRACE( "clipping to %s win %lx\n", wine_dbgstr_rect(clip), clip_window );
 
     if (!data->clipping_cursor) XUnmapWindow( data->display, clip_window );
@@ -421,11 +422,7 @@ static BOOL grab_clipping_window( const RECT *clip )
 
     set_window_cursor( clip_window, cursor );
 
-    if (!clipping_cursor)
-    {
-        x11drv_xinput2_disable( data->display, DefaultRootWindow( data->display ) );
-        return FALSE;
-    }
+    if (!clipping_cursor) return FALSE;
     clip_rect = *clip;
     data->clipping_cursor = TRUE;
     return TRUE;
@@ -452,7 +449,6 @@ void ungrab_clipping_window(void)
     if (clipping_cursor) XUngrabPointer( data->display, CurrentTime );
     clipping_cursor = FALSE;
     data->clipping_cursor = FALSE;
-    x11drv_xinput2_disable( data->display, DefaultRootWindow( data->display ) );
 }
 
 /***********************************************************************
@@ -481,82 +477,58 @@ static BOOL is_old_motion_event( unsigned long serial )
 }
 
 
-/***********************************************************************
- *		map_event_coords
- *
- * Map the input event coordinates so they're relative to the desktop.
- */
-static void map_event_coords( HWND hwnd, Window window, Window event_root, int x_root, int y_root, INPUT *input )
+/* Map the input event coordinates so they're relative to the desktop. */
+static POINT map_event_coords( HWND hwnd, Window window, Window event_root, POINT root, POINT src )
 {
     struct x11drv_thread_data *thread_data;
     struct x11drv_win_data *data;
-    POINT pt = { input->mi.dx, input->mi.dy };
+    POINT dst = src;
 
-    TRACE( "hwnd %p, window %lx, event_root %lx, x_root %d, y_root %d, input %p\n", hwnd, window, event_root,
-           x_root, y_root, input );
+    TRACE( "hwnd %p, window %lx, event_root %lx, root %s, src %s\n", hwnd, window, event_root,
+           wine_dbgstr_point( &root ), wine_dbgstr_point( &src ) );
 
-    if (window == root_window) pt = root_to_virtual_screen( pt.x, pt.y );
-    else if (event_root == root_window) pt = root_to_virtual_screen( x_root, y_root );
+    if (window == root_window) dst = root_to_virtual_screen( src.x, src.y );
+    else if (event_root == root_window) dst = root_to_virtual_screen( root.x, root.y );
     else if (!hwnd)
     {
         thread_data = x11drv_thread_data();
-        if (!thread_data->clipping_cursor) return;
-        if (thread_data->clip_window != window) return;
-        pt.x += clip_rect.left;
-        pt.y += clip_rect.top;
+        if (!thread_data->clipping_cursor) return dst;
+        if (thread_data->clip_window != window) return dst;
+        dst.x += clip_rect.left;
+        dst.y += clip_rect.top;
     }
     else if ((data = get_win_data( hwnd )))
     {
         if (window == data->client_window)
         {
-            pt.x += data->rects.client.left;
-            pt.y += data->rects.client.top;
+            dst.x += data->rects.client.left;
+            dst.y += data->rects.client.top;
         }
         else
         {
-            pt.x += data->rects.visible.left;
-            pt.y += data->rects.visible.top;
+            dst.x += data->rects.visible.left;
+            dst.y += data->rects.visible.top;
         }
         release_win_data( data );
     }
 
-    TRACE( "mapped %s to %s\n", wine_dbgstr_point( (POINT *)&input->mi.dx ), wine_dbgstr_point( &pt ) );
-
-    input->mi.dx = pt.x;
-    input->mi.dy = pt.y;
+    TRACE( "mapped %s to %s\n", wine_dbgstr_point( &src ), wine_dbgstr_point( &dst ) );
+    return dst;
 }
 
-/***********************************************************************
- *		send_mouse_input
- *
- * Update the various window states on a mouse event.
- */
-static void send_mouse_input( HWND hwnd, Window window, unsigned int state, INPUT *input )
+static void send_mouse_input( HWND hwnd, POINT pos, UINT flags, UINT data, UINT time, const struct raw_mouse *raw )
 {
-    struct x11drv_thread_data *thread_data = x11drv_thread_data();
-
-    /* ignore clipping window input when not clipping or wrong clipping window */
-    if (!hwnd && (!thread_data->clipping_cursor || thread_data->clip_window != window)) return;
+    INPUT input = { .type = INPUT_MOUSE };
 
-    /* update the wine server Z-order */
-
-    if (hwnd && hwnd != NtUserGetAncestor( get_capture_window(), GA_ROOT ) &&
-        /* ignore event if a button is pressed, since the mouse is then grabbed too */
-        !(state & (Button1Mask|Button2Mask|Button3Mask|Button4Mask|Button5Mask|Button6Mask|Button7Mask)))
-    {
-        RECT rect = { input->mi.dx, input->mi.dy, input->mi.dx + 1, input->mi.dy + 1 };
+    if ((flags & MOUSEEVENTF_ABSOLUTE) || pos.x || pos.y) flags |= MOUSEEVENTF_MOVE;
 
-        SERVER_START_REQ( update_window_zorder )
-        {
-            req->window      = wine_server_user_handle( hwnd );
-            req->rect        = wine_server_rectangle( rect );
-            wine_server_call( req );
-        }
-        SERVER_END_REQ;
-    }
+    input.mi.dx = pos.x;
+    input.mi.dy = pos.y;
+    input.mi.dwFlags = flags;
+    input.mi.mouseData = data;
+    input.mi.time = time;
 
-    input->type = INPUT_MOUSE;
-    NtUserSendHardwareInput( hwnd, 0, input, 0 );
+    NtUserSendHardwareInput( hwnd, SEND_HWMSG_RAWINPUT, &input, (LPARAM)raw );
 }
 
 #ifdef SONAME_LIBXCURSOR
@@ -1473,30 +1445,20 @@ void move_resize_window( HWND hwnd, int dir, POINT pos )
     /* try to detect the end of the size/move by polling for the mouse button to be released */
     /* (some apps don't like it if we return before the size/move is done) */
 
-    if (!button) return;
+    if (!button--) return;
     send_message( hwnd, WM_ENTERSIZEMOVE, 0, 0 );
 
     for (;;)
     {
         MSG msg;
-        INPUT input;
         int x, y, rootX, rootY;
+        UINT flags = button_up_flags[button];
 
         if (!XQueryPointer( display, root_window, &root, &child, &rootX, &rootY, &x, &y, &xstate )) break;
 
-        if (!(xstate & (Button1Mask << (button - 1))))
-        {
-            /* fake a button release event */
-            pos = root_to_virtual_screen( x, y );
-            input.type = INPUT_MOUSE;
-            input.mi.dx          = pos.x;
-            input.mi.dy          = pos.y;
-            input.mi.mouseData   = button_up_data[button - 1];
-            input.mi.dwFlags     = button_up_flags[button - 1] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
-            input.mi.time        = NtGetTickCount();
-            input.mi.dwExtraInfo = 0;
-            NtUserSendHardwareInput( hwnd, 0, &input, 0 );
-        }
+        if (!(xstate & (Button1Mask << button))) /* fake a button release event */
+            send_mouse_input( hwnd, root_to_virtual_screen( x, y ), MOUSEEVENTF_ABSOLUTE | flags,
+                              button_up_data[button], NtGetTickCount(), NULL );
 
         while (NtUserPeekMessage( &msg, 0, 0, 0, PM_REMOVE ))
         {
@@ -1507,7 +1469,7 @@ void move_resize_window( HWND hwnd, int dir, POINT pos )
             }
         }
 
-        if (!(xstate & (Button1Mask << (button - 1)))) break;
+        if (!(xstate & (Button1Mask << button))) break;
         NtUserMsgWaitForMultipleObjectsEx( 0, NULL, 100, QS_ALLINPUT, 0 );
     }
 
@@ -1522,20 +1484,14 @@ void move_resize_window( HWND hwnd, int dir, POINT pos )
 BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
 {
     XButtonEvent *event = &xev->xbutton;
-    int buttonNum = event->button - 1;
+    UINT button = event->button - 1, flags, time = EVENT_x11_time_to_win32_time( event->time );
+    POINT pt = { event->x, event->y }, root = { event->x_root, event->y_root };
     struct x11drv_win_data *data;
-    INPUT input;
 
-    if (buttonNum >= NB_BUTTONS) return FALSE;
+    if (button >= NB_BUTTONS) return FALSE;
+    flags = button_down_flags[button];
 
-    TRACE( "hwnd %p/%lx button %u pos %d,%d\n", hwnd, event->window, buttonNum, event->x, event->y );
-
-    input.mi.dx          = event->x;
-    input.mi.dy          = event->y;
-    input.mi.mouseData   = button_down_data[buttonNum];
-    input.mi.dwFlags     = button_down_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
-    input.mi.time        = EVENT_x11_time_to_win32_time( event->time );
-    input.mi.dwExtraInfo = 0;
+    TRACE( "hwnd %p/%lx button %u pos %s\n", hwnd, event->window, button, wine_dbgstr_point( &pt ) );
 
     if ((data = get_win_data( hwnd )))
     {
@@ -1543,8 +1499,8 @@ BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
         release_win_data( data );
     }
 
-    map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input );
-    send_mouse_input( hwnd, event->window, event->state, &input );
+    pt = map_event_coords( hwnd, event->window, event->root, root, pt );
+    send_mouse_input( hwnd, pt, MOUSEEVENTF_ABSOLUTE | flags, button_down_data[button], time, NULL );
     return TRUE;
 }
 
@@ -1555,22 +1511,15 @@ BOOL X11DRV_ButtonPress( HWND hwnd, XEvent *xev )
 BOOL X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
 {
     XButtonEvent *event = &xev->xbutton;
-    int buttonNum = event->button - 1;
-    INPUT input;
-
-    if (buttonNum >= NB_BUTTONS || !button_up_flags[buttonNum]) return FALSE;
+    UINT button = event->button - 1, flags, time = EVENT_x11_time_to_win32_time( event->time );
+    POINT pt = { event->x, event->y }, root = { event->x_root, event->y_root };
 
-    TRACE( "hwnd %p/%lx button %u pos %d,%d\n", hwnd, event->window, buttonNum, event->x, event->y );
+    if (button >= NB_BUTTONS || !(flags = button_up_flags[button])) return FALSE;
 
-    input.mi.dx          = event->x;
-    input.mi.dy          = event->y;
-    input.mi.mouseData   = button_up_data[buttonNum];
-    input.mi.dwFlags     = button_up_flags[buttonNum] | MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
-    input.mi.time        = EVENT_x11_time_to_win32_time( event->time );
-    input.mi.dwExtraInfo = 0;
+    TRACE( "hwnd %p/%lx button %u pos %s\n", hwnd, event->window, button, wine_dbgstr_point( &pt ) );
 
-    map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input );
-    send_mouse_input( hwnd, event->window, event->state, &input );
+    pt = map_event_coords( hwnd, event->window, event->root, root, pt );
+    send_mouse_input( hwnd, pt, MOUSEEVENTF_ABSOLUTE | flags, button_up_data[button], time, NULL );
     return TRUE;
 }
 
@@ -1581,25 +1530,20 @@ BOOL X11DRV_ButtonRelease( HWND hwnd, XEvent *xev )
 BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
 {
     XMotionEvent *event = &xev->xmotion;
-    INPUT input;
+    POINT pt = { event->x, event->y }, root = { event->x_root, event->y_root };
+    UINT time = EVENT_x11_time_to_win32_time( event->time );
 
-    TRACE( "hwnd %p/%lx pos %d,%d is_hint %d serial %lu\n",
-           hwnd, event->window, event->x, event->y, event->is_hint, event->serial );
-
-    input.mi.dx          = event->x;
-    input.mi.dy          = event->y;
-    input.mi.mouseData   = 0;
-    input.mi.dwFlags     = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
-    input.mi.time        = EVENT_x11_time_to_win32_time( event->time );
-    input.mi.dwExtraInfo = 0;
+    TRACE( "hwnd %p/%lx pos %s is_hint %d serial %lu\n", hwnd, event->window, wine_dbgstr_point( &pt ),
+           event->is_hint, event->serial );
 
     if (is_old_motion_event( event->serial ))
     {
         TRACE( "pos %d,%d old serial %lu, ignoring\n", event->x, event->y, event->serial );
         return FALSE;
     }
-    map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input );
-    send_mouse_input( hwnd, event->window, event->state, &input );
+
+    pt = map_event_coords( hwnd, event->window, event->root, root, pt );
+    send_mouse_input( hwnd, pt, MOUSEEVENTF_ABSOLUTE, 0, time, NULL );
     return TRUE;
 }
 
@@ -1610,29 +1554,23 @@ BOOL X11DRV_MotionNotify( HWND hwnd, XEvent *xev )
 BOOL X11DRV_EnterNotify( HWND hwnd, XEvent *xev )
 {
     XCrossingEvent *event = &xev->xcrossing;
-    INPUT input;
+    POINT pt = { event->x, event->y }, root = { event->x_root, event->y_root };
+    UINT time = EVENT_x11_time_to_win32_time( event->time );
 
-    TRACE( "hwnd %p/%lx pos %d,%d detail %d\n", hwnd, event->window, event->x, event->y, event->detail );
+    TRACE( "hwnd %p/%lx pos %s detail %d\n", hwnd, event->window, wine_dbgstr_point( &pt ), event->detail );
 
     x11drv_thread_data()->keymapnotify_hwnd = hwnd;
 
     if (hwnd == NtUserGetAncestor( get_capture_window(), GA_ROOT )) return FALSE;
 
-    /* simulate a mouse motion event */
-    input.mi.dx          = event->x;
-    input.mi.dy          = event->y;
-    input.mi.mouseData   = 0;
-    input.mi.dwFlags     = MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE;
-    input.mi.time        = EVENT_x11_time_to_win32_time( event->time );
-    input.mi.dwExtraInfo = 0;
-
     if (is_old_motion_event( event->serial ))
     {
         TRACE( "pos %d,%d old serial %lu, ignoring\n", event->x, event->y, event->serial );
         return FALSE;
     }
-    map_event_coords( hwnd, event->window, event->root, event->x_root, event->y_root, &input );
-    send_mouse_input( hwnd, event->window, event->state, &input );
+
+    pt = map_event_coords( hwnd, event->window, event->root, root, pt );
+    send_mouse_input( hwnd, pt, MOUSEEVENTF_ABSOLUTE, 0, time, NULL );
     return TRUE;
 }
 
@@ -1651,19 +1589,20 @@ static BOOL X11DRV_DeviceChanged( XGenericEventCookie *xev )
     return TRUE;
 }
 
-static BOOL map_raw_event_coords( XIRawEvent *event, INPUT *input )
+static POINT map_raw_event_coords( XIRawEvent *event, POINT *raw )
 {
     struct x11drv_thread_data *thread_data = x11drv_thread_data();
     XIValuatorClassInfo *x = &thread_data->x_valuator, *y = &thread_data->y_valuator;
+    const double *values = event->valuators.values, *raw_values = event->raw_values;
     double x_value = 0, y_value = 0, x_scale, y_scale;
-    const double *values = event->valuators.values;
+    POINT pt = { 0, 0 };
     RECT virtual_rect;
     int i;
 
-    if (x->number < 0 || y->number < 0) return FALSE;
-    if (!event->valuators.mask_len) return FALSE;
-    if (!xinput2_available) return FALSE;
-    if (event->deviceid != thread_data->xinput2_pointer) return FALSE;
+    if (x->number < 0 || y->number < 0) return pt;
+    if (!event->valuators.mask_len) return pt;
+    if (!xinput2_available) return pt;
+    if (event->deviceid != thread_data->xinput2_pointer) return pt;
 
     virtual_rect = NtUserGetVirtualScreenRect( MDT_RAW_DPI );
 
@@ -1677,33 +1616,39 @@ static BOOL map_raw_event_coords( XIRawEvent *event, INPUT *input )
         if (!XIMaskIsSet( event->valuators.mask, i )) continue;
         if (i == x->number)
         {
+            raw->x = *raw_values;
             x_value = *values;
             x->value += x_value * x_scale;
         }
         if (i == y->number)
         {
+            raw->y = *raw_values;
             y_value = *values;
             y->value += y_value * y_scale;
         }
+        raw_values++;
         values++;
     }
 
-    input->mi.dx = round( x->value );
-    input->mi.dy = round( y->value );
+    /* when not clipping cursor, we use MotionNotify for absolute mouse position */
+    if (!thread_data->clipping_cursor)
+    {
+        TRACE( "received raw frame %s\n", wine_dbgstr_point( raw ) );
+        x->value = y->value = 0;
+        return pt;
+    }
 
-    TRACE( "event %f,%f value %f,%f input %d,%d\n", x_value, y_value, x->value, y->value,
-           input->mi.dx, input->mi.dy );
+    pt.x = round( x->value );
+    pt.y = round( y->value );
 
-    x->value -= input->mi.dx;
-    y->value -= input->mi.dy;
+    TRACE( "event %f,%f value %f,%f input %s, raw %s\n", x_value, y_value, x->value, y->value,
+           wine_dbgstr_point( &pt ), wine_dbgstr_point( raw ) );
 
-    if (!input->mi.dx && !input->mi.dy)
-    {
-        TRACE( "accumulating motion\n" );
-        return FALSE;
-    }
+    x->value -= pt.x;
+    y->value -= pt.y;
 
-    return TRUE;
+    if (!pt.x && !pt.y) TRACE( "accumulating motion\n" );
+    return pt;
 }
 
 /***********************************************************************
@@ -1712,7 +1657,9 @@ static BOOL map_raw_event_coords( XIRawEvent *event, INPUT *input )
 static BOOL X11DRV_RawMotion( XGenericEventCookie *xev )
 {
     XIRawEvent *event = xev->data;
-    INPUT input;
+    UINT time = EVENT_x11_time_to_win32_time( event->time );
+    struct raw_mouse raw = { .count = 1 };
+    POINT pt;
 
     if (broken_rawevents && is_old_motion_event( xev->serial ))
     {
@@ -1720,16 +1667,8 @@ static BOOL X11DRV_RawMotion( XGenericEventCookie *xev )
         return FALSE;
     }
 
-    input.type = INPUT_MOUSE;
-    input.mi.mouseData   = 0;
-    input.mi.dwFlags     = MOUSEEVENTF_MOVE;
-    input.mi.time        = EVENT_x11_time_to_win32_time( event->time );
-    input.mi.dwExtraInfo = 0;
-    input.mi.dx          = 0;
-    input.mi.dy          = 0;
-    if (!map_raw_event_coords( event, &input )) return FALSE;
-
-    NtUserSendHardwareInput( 0, 0, &input, 0 );
+    pt = map_raw_event_coords( event, raw.data );
+    send_mouse_input( NULL, pt, 0, 0, time, &raw );
     return TRUE;
 }
 
@@ -1738,14 +1677,13 @@ static BOOL X11DRV_TouchEvent( HWND hwnd, XGenericEventCookie *xev )
     RECT virtual = NtUserGetVirtualScreenRect( MDT_RAW_DPI );
     INPUT input = {.type = INPUT_HARDWARE};
     XIDeviceEvent *event = xev->data;
+    POINT pt = { event->event_x, event->event_y }, root = { event->root_x, event->root_y };
     int flags = 0;
     POINT pos;
 
-    input.mi.dx = event->event_x;
-    input.mi.dy = event->event_y;
-    map_event_coords( hwnd, event->event, event->root, event->root_x, event->root_y, &input );
-    pos.x = input.mi.dx * 65535 / (virtual.right - virtual.left);
-    pos.y = input.mi.dy * 65535 / (virtual.bottom - virtual.top);
+    pt = map_event_coords( hwnd, event->event, event->root, root, pt );
+    pos.x = pt.x * 65535 / (virtual.right - virtual.left);
+    pos.y = pt.y * 65535 / (virtual.bottom - virtual.top);
 
     switch (event->evtype)
     {
diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c
index 2d5608754699..9771b4d33fa8 100644
--- a/dlls/winex11.drv/opengl.c
+++ b/dlls/winex11.drv/opengl.c
@@ -1197,7 +1197,7 @@ static void x11drv_surface_flush( struct opengl_drawable *base, UINT flags )
 /***********************************************************************
  *		X11DRV_wglCreateContextAttribsARB
  */
-static BOOL x11drv_context_create( int format, void *share, const int *attribList, void **context )
+static BOOL x11drv_context_create( int format, void *share, const int *attribList, void **context, BOOL *shared )
 {
     int glx_attribs[16] = {0}, *pContextAttribList = glx_attribs;
     int err = 0;
diff --git a/dlls/winex11.drv/window.c b/dlls/winex11.drv/window.c
index 3b964baadef5..297695749a16 100644
--- a/dlls/winex11.drv/window.c
+++ b/dlls/winex11.drv/window.c
@@ -147,7 +147,7 @@ static const char *debugstr_monitor_indices( const struct monitor_indices *monit
     return wine_dbg_sprintf( "%ld,%ld,%ld,%ld", monitors->indices[0], monitors->indices[1], monitors->indices[2], monitors->indices[3] );
 }
 
-static pthread_mutex_t win_data_mutex = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t win_data_mutex;
 
 static void host_window_add_ref( struct host_window *win )
 {
@@ -3274,7 +3274,7 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN
            debugstr_window_rects(new_rects), new_style, swp_flags );
 
     /* visible windows are only hidden after SWP_HIDEWINDOW is used */
-    if (data->pending_state.wm_state != WithdrawnState && !(new_style & WS_VISIBLE) &&
+    if (data->desired_state.wm_state != WithdrawnState && !(new_style & WS_VISIBLE) &&
         !(swp_flags & SWP_HIDEWINDOW))
     {
         WARN( "win %p/%lx not yet hidden, delaying unmapping\n", hwnd, data->whole_window );
@@ -3282,7 +3282,7 @@ void X11DRV_WindowPosChanged( HWND hwnd, HWND insert_after, HWND owner_hint, UIN
     }
 
     /* layered windows are mapped only once their attributes are set */
-    if (data->pending_state.wm_state == WithdrawnState && (new_style & WS_VISIBLE) &&
+    if (data->desired_state.wm_state == WithdrawnState && (new_style & WS_VISIBLE) &&
         (ex_style & WS_EX_LAYERED) && !data->layered && !IsRectEmpty( &new_rects->window ))
     {
         WARN( "win %p/%lx is layered, delaying mapping\n", hwnd, data->whole_window );
diff --git a/dlls/winex11.drv/x11drv.h b/dlls/winex11.drv/x11drv.h
index 73d778259e5f..4325c99e5c5d 100644
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -371,7 +371,6 @@ struct x11drv_client_surface
     XWindowChanges changes;
     Colormap colormap;
     Window window;
-    RECT rect;
 
     HDC hdc_src;
     HDC hdc_dst;
@@ -431,6 +430,7 @@ struct x11drv_thread_data
     XIValuatorClassInfo x_valuator;
     XIValuatorClassInfo y_valuator;
     int      xinput2_pointer;      /* XInput2 master pointer device id */
+    int      root_window_users;    /* counter for root window XISelectEvents */
 #endif /* HAVE_X11_EXTENSIONS_XINPUT2_H */
 
     struct display_state desired_state;       /* display state tracking the desired / win32 state */
diff --git a/dlls/wininet/tests/urlcache.c b/dlls/wininet/tests/urlcache.c
index b1296d34c7a6..0cf86d2a7829 100644
--- a/dlls/wininet/tests/urlcache.c
+++ b/dlls/wininet/tests/urlcache.c
@@ -1006,6 +1006,21 @@ static void test_urlcacheW(void)
             ok(ret, "%ld) DeleteUrlCacheEntryW failed: %ld\n", i, GetLastError());
         }
     }
+
+    /* CreateUrlCacheEntryW has no output buffer size parameter, so it must not
+     * write more than MAX_PATH characters to lpszFileName.
+     */
+    for(i=0; i<ARRAY_SIZE(bufW) -1; i++)
+        bufW[i] = 'a';
+    bufW[i] = 0;
+
+    ret = CreateUrlCacheEntryW(test_urlW, 0, bufW, bufW, 0);
+    ok(ret, "CreateUrlCacheEntryW failed: %ld\n", GetLastError());
+    if(ret) {
+        ok(lstrlenW(bufW) < MAX_PATH, "cache path too long: %s\n", wine_dbgstr_w(bufW));
+        ret = DeleteFileW(bufW);
+        ok(ret, "DeleteFileW failed: %ld\n", GetLastError());
+    }
 }
 
 static void test_FindCloseUrlCache(void)
diff --git a/dlls/wininet/urlcache.c b/dlls/wininet/urlcache.c
index 49c8ce9becc9..933e33e6834f 100644
--- a/dlls/wininet/urlcache.c
+++ b/dlls/wininet/urlcache.c
@@ -2689,19 +2689,23 @@ static BOOL urlcache_entry_create(const char *url, const char *ext, WCHAR *full_
 
         extW[0] = '.';
         ext_len = MultiByteToWideChar(CP_ACP, 0, ext, -1, extW+1, MAX_PATH-1);
-
-        for(p=extW; *p; p++) {
-            switch(*p) {
-            case '<': case '>':
-            case ':': case '"':
-            case '|': case '?':
-            case '*':
-                *p = '_'; break;
-            default: break;
+        if(!ext_len || ext_len >= MAX_PATH - 8) {
+            extW[0] = '\0';
+            ext_len = 0;
+        }else {
+            for(p=extW; *p; p++) {
+                switch(*p) {
+                case '<': case '>':
+                case ':': case '"':
+                case '|': case '?':
+                case '*':
+                    *p = '_'; break;
+                default: break;
+                }
             }
+            if(p > extW && (p[-1]==' ' || p[-1]=='.'))
+                p[-1] = '_';
         }
-        if(p[-1]==' ' || p[-1]=='.')
-            p[-1] = '_';
     }else {
         extW[0] = '\0';
     }
diff --git a/dlls/wow64/process.c b/dlls/wow64/process.c
index 625b0eaff75c..98d1c600dd93 100644
--- a/dlls/wow64/process.c
+++ b/dlls/wow64/process.c
@@ -927,13 +927,18 @@ NTSTATUS WINAPI wow64_NtSetInformationProcess( UINT *args )
     case ProcessPriorityClass:   /* PROCESS_PRIORITY_CLASS */
     case ProcessBasePriority:   /* ULONG */
     case ProcessPriorityBoost:  /* ULONG */
-    case ProcessExecuteFlags:   /* ULONG */
     case ProcessPagePriority:   /* MEMORY_PRIORITY_INFORMATION */
     case ProcessPowerThrottlingState:   /* PROCESS_POWER_THROTTLING_STATE */
     case ProcessLeapSecondInformation:   /* PROCESS_LEAP_SECOND_INFO */
     case ProcessWineGrantAdminToken:   /* NULL */
         return NtSetInformationProcess( handle, class, ptr, len );
 
+    case ProcessExecuteFlags:   /* ULONG */
+        status = NtSetInformationProcess( handle, class, ptr, len );
+        if (!status && pBTCpuNotifyProcessExecuteFlagsChange)
+            pBTCpuNotifyProcessExecuteFlagsChange(*(ULONG *)ptr);
+        return status;
+
     case ProcessAccessToken: /* PROCESS_ACCESS_TOKEN */
         if (len == sizeof(PROCESS_ACCESS_TOKEN32))
         {
diff --git a/dlls/wow64/syscall.c b/dlls/wow64/syscall.c
index d52c52fef8e9..cfeb8f4ae6bb 100644
--- a/dlls/wow64/syscall.c
+++ b/dlls/wow64/syscall.c
@@ -111,6 +111,7 @@ void     (WINAPI *pBTCpuNotifyMemoryAlloc)( void *, SIZE_T, ULONG, ULONG, BOOL,
 void     (WINAPI *pBTCpuNotifyMemoryDirty)( void *, SIZE_T ) = NULL;
 void     (WINAPI *pBTCpuNotifyMemoryFree)( void *, SIZE_T, ULONG, BOOL, NTSTATUS ) = NULL;
 void     (WINAPI *pBTCpuNotifyMemoryProtect)( void *, SIZE_T, ULONG, BOOL, NTSTATUS ) = NULL;
+void     (WINAPI *pBTCpuNotifyProcessExecuteFlagsChange)(ULONG) = NULL;
 void     (WINAPI *pBTCpuNotifyReadFile)( HANDLE, void *, SIZE_T, BOOL, NTSTATUS ) = NULL;
 void     (WINAPI *pBTCpuNotifyUnmapViewOfSection)( void *, BOOL, NTSTATUS ) = NULL;
 NTSTATUS (WINAPI *pBTCpuResetToConsistentState)( EXCEPTION_POINTERS * ) = NULL;
@@ -1023,6 +1024,7 @@ static DWORD WINAPI process_init( RTL_RUN_ONCE *once, void *param, void **contex
     GET_PTR( BTCpuNotifyMemoryDirty );
     GET_PTR( BTCpuNotifyMemoryFree );
     GET_PTR( BTCpuNotifyMemoryProtect );
+    GET_PTR( BTCpuNotifyProcessExecuteFlagsChange );
     GET_PTR( BTCpuNotifyReadFile );
     GET_PTR( BTCpuNotifyUnmapViewOfSection );
     GET_PTR( BTCpuUpdateProcessorInformation );
diff --git a/dlls/wow64/wow64_private.h b/dlls/wow64/wow64_private.h
index 2ac3e51f1f0a..b3ea679cda65 100644
--- a/dlls/wow64/wow64_private.h
+++ b/dlls/wow64/wow64_private.h
@@ -46,6 +46,7 @@ extern void     (WINAPI *pBTCpuNotifyMemoryAlloc)( void *, SIZE_T, ULONG, ULONG,
 extern void     (WINAPI *pBTCpuNotifyMemoryDirty)( void *, SIZE_T );
 extern void     (WINAPI *pBTCpuNotifyMemoryFree)( void *, SIZE_T, ULONG, BOOL, NTSTATUS );
 extern void     (WINAPI *pBTCpuNotifyMemoryProtect)( void *, SIZE_T, ULONG, BOOL, NTSTATUS );
+extern void     (WINAPI *pBTCpuNotifyProcessExecuteFlagsChange)(ULONG);
 extern void     (WINAPI *pBTCpuNotifyReadFile)( HANDLE, void *, SIZE_T, BOOL, NTSTATUS );
 extern void     (WINAPI *pBTCpuNotifyUnmapViewOfSection)( void *, BOOL, NTSTATUS );
 extern void     (WINAPI *pBTCpuUpdateProcessorInformation)( SYSTEM_CPU_INFORMATION * );
diff --git a/dlls/ws2_32/protocol.c b/dlls/ws2_32/protocol.c
index 647409d84a3c..cdc01a82683f 100644
--- a/dlls/ws2_32/protocol.c
+++ b/dlls/ws2_32/protocol.c
@@ -2389,9 +2389,20 @@ int WINAPI WSCSetApplicationCategory( const WCHAR *path, DWORD len, const WCHAR
  */
 int WINAPI WSCEnumProtocols( int *protocols, WSAPROTOCOL_INFOW *info, DWORD *len, int *err )
 {
-    int ret = WSAEnumProtocolsW( protocols, info, len );
+    int ret;
 
-    if (ret == SOCKET_ERROR) *err = WSAENOBUFS;
+    TRACE( "protocols %p, info %p, len %p, err %p.\n", protocols, info, len, err );
 
+    ret = WSAEnumProtocolsW( protocols, info, len );
+    if (ret == SOCKET_ERROR) *err = WSAENOBUFS;
     return ret;
 }
+
+
+/***********************************************************************
+ *      WSCEnumProtocols32   (ws2_32.@)
+ */
+int WINAPI WSCEnumProtocols32( int *protocols, WSAPROTOCOL_INFOW *info, DWORD *len, int *err )
+{
+    return WSCEnumProtocols( protocols, info, len, err );
+}
diff --git a/dlls/ws2_32/tests/protocol.c b/dlls/ws2_32/tests/protocol.c
index 0e87ee04ce51..76339c0a1464 100644
--- a/dlls/ws2_32/tests/protocol.c
+++ b/dlls/ws2_32/tests/protocol.c
@@ -44,7 +44,6 @@ static int (WINAPI *pGetAddrInfoExW)(const WCHAR *name, const WCHAR *servname, D
 static int   (WINAPI *pGetAddrInfoExOverlappedResult)(OVERLAPPED *overlapped);
 static int (WINAPI *pGetHostNameW)(WCHAR *name, int len);
 static const char *(WINAPI *p_inet_ntop)(int family, void *addr, char *string, ULONG size);
-static const WCHAR *(WINAPI *pInetNtopW)(int family, void *addr, WCHAR *string, ULONG size);
 static int (WINAPI *p_inet_pton)(int family, const char *string, void *addr);
 static int (WINAPI *pInetPtonW)(int family, WCHAR *string, void *addr);
 static int (WINAPI *pWSCGetApplicationCategory)(LPCWSTR path, DWORD path_len, LPCWSTR extra, DWORD extra_len, DWORD *category, INT *err);
@@ -3173,7 +3172,6 @@ START_TEST( protocol )
     pGetAddrInfoExW = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "GetAddrInfoExW");
     pGetHostNameW = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "GetHostNameW");
     p_inet_ntop = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "inet_ntop");
-    pInetNtopW = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "InetNtopW");
     p_inet_pton = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "inet_pton");
     pInetPtonW = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "InetPtonW");
     pWSCGetApplicationCategory = (void *)GetProcAddress(GetModuleHandleA("ws2_32"), "WSCGetApplicationCategory");
diff --git a/dlls/ws2_32/ws2_32.spec b/dlls/ws2_32/ws2_32.spec
index 1bdcd60cc7be..1e5e0b3b8731 100644
--- a/dlls/ws2_32/ws2_32.spec
+++ b/dlls/ws2_32/ws2_32.spec
@@ -121,6 +121,7 @@
 @ stdcall WSCDeinstallProvider(ptr ptr)
 @ stdcall WSCEnableNSProvider(ptr long)
 @ stdcall WSCEnumProtocols(ptr ptr ptr ptr)
+@ stdcall WSCEnumProtocols32(ptr ptr ptr ptr)
 @ stdcall WSCGetApplicationCategory(wstr long wstr long ptr ptr)
 @ stdcall WSCGetProviderInfo(ptr long ptr ptr long ptr)
 @ stdcall WSCGetProviderPath(ptr ptr ptr ptr)
diff --git a/include/Makefile.in b/include/Makefile.in
index 47df75fb59e1..72a8d43988ce 100644
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -801,6 +801,7 @@ SOURCES = \
 	powersetting.h \
 	powrprof.h \
 	prntvpt.h \
+	processsnapshot.h \
 	processthreadsapi.h \
 	profinfo.h \
 	proofofpossessioncookieinfo.idl \
diff --git a/include/config.h.in b/include/config.h.in
index 831e35a23d9c..34f193df1d0b 100644
--- a/include/config.h.in
+++ b/include/config.h.in
@@ -12,6 +12,9 @@
 /* Define to 1 if you have the <arpa/nameser.h> header file. */
 #undef HAVE_ARPA_NAMESER_H
 
+/* Define to 1 if you have the <asm/hwcap.h> header file. */
+#undef HAVE_ASM_HWCAP_H
+
 /* Define to 1 if you have the <asm/termbits.h> header file. */
 #undef HAVE_ASM_TERMBITS_H
 
@@ -42,6 +45,9 @@
 /* Define to 1 if you have the <cups/ppd.h> header file. */
 #undef HAVE_CUPS_PPD_H
 
+/* Define to 1 if you have the <dev/evdev/input.h> header file. */
+#undef HAVE_DEV_EVDEV_INPUT_H
+
 /* Define to 1 if you have the 'dladdr1' function. */
 #undef HAVE_DLADDR1
 
diff --git a/include/ddk/fltkernel.h b/include/ddk/fltkernel.h
index e5483d5a3fa2..ce7f16e4ab85 100644
--- a/include/ddk/fltkernel.h
+++ b/include/ddk/fltkernel.h
@@ -26,6 +26,7 @@ extern "C" {
 
 typedef struct _FLT_FILTER   *PFLT_FILTER;
 typedef struct _FLT_INSTANCE *PFLT_INSTANCE;
+typedef struct _FLT_VOLUME   *PFLT_VOLUME;
 typedef ULONG  FLT_CALLBACK_DATA_FLAGS;
 typedef USHORT FLT_CONTEXT_REGISTRATION_FLAGS;
 typedef USHORT FLT_CONTEXT_TYPE;
@@ -585,6 +586,8 @@ typedef struct _FLT_CALLBACK_DATA
     KPROCESSOR_MODE RequestorMode;
 } FLT_CALLBACK_DATA, *PFLT_CALLBACK_DATA;
 
+typedef struct _FLT_PORT *PFLT_PORT;
+
 typedef void*    (__WINE_ALLOC_SIZE(2) WINAPI *PFLT_CONTEXT_ALLOCATE_CALLBACK)(POOL_TYPE,SIZE_T,FLT_CONTEXT_TYPE);
 typedef void     (WINAPI *PFLT_CONTEXT_CLEANUP_CALLBACK)(PFLT_CONTEXT, FLT_CONTEXT_TYPE);
 typedef void     (WINAPI *PFLT_CONTEXT_FREE_CALLBACK)(void *, FLT_CONTEXT_TYPE);
@@ -609,6 +612,11 @@ typedef FLT_POSTOP_CALLBACK_STATUS (WINAPI *PFLT_POST_OPERATION_CALLBACK)(PFLT_C
 typedef NTSTATUS (WINAPI *PFLT_SECTION_CONFLICT_NOTIFICATION_CALLBACK)(PFLT_INSTANCE, PFLT_CONTEXT,
                 PFLT_CALLBACK_DATA);
 typedef NTSTATUS (WINAPI *PFLT_TRANSACTION_NOTIFICATION_CALLBACK)(PCFLT_RELATED_OBJECTS, PFLT_CONTEXT, ULONG);
+typedef void (WINAPI *PFLT_COMPLETED_ASYNC_IO_CALLBACK)(PFLT_CALLBACK_DATA,PFLT_CONTEXT Context);
+typedef NTSTATUS (WINAPI *PFLT_CONNECT_NOTIFY)(PFLT_PORT, PVOID, PVOID, ULONG, PVOID *);
+typedef NTSTATUS (WINAPI *PFLT_MESSAGE_NOTIFY)(PVOID,PVOID,ULONG,PVOID,ULONG,PULONG);
+typedef void (WINAPI *PFLT_DISCONNECT_NOTIFY)(PVOID);
+
 
 typedef struct _FLT_CONTEXT_REGISTRATION
 {
@@ -653,10 +661,59 @@ typedef struct _FLT_REGISTRATION
     PFLT_SECTION_CONFLICT_NOTIFICATION_CALLBACK SectionNotificationCallback;
 } FLT_REGISTRATION, *PFLT_REGISTRATION;
 
+typedef enum _FLT_SET_CONTEXT_OPERATION
+{
+    FLT_SET_CONTEXT_REPLACE_IF_EXISTS,
+    FLT_SET_CONTEXT_KEEP_IF_EXISTS
+} FLT_SET_CONTEXT_OPERATION, *PFLT_SET_CONTEXT_OPERATION;
+
+typedef USHORT FLT_FILE_NAME_PARSED_FLAGS;
+
+#define FLTFL_FILE_NAME_PARSED_FINAL_COMPONENT 0x0001
+#define FLTFL_FILE_NAME_PARSED_EXTENSION       0x0002
+#define FLTFL_FILE_NAME_PARSED_STREAM          0x0004
+#define FLTFL_FILE_NAME_PARSED_PARENT_DIR      0x0008
+
+typedef ULONG FLT_IO_OPERATION_FLAGS;
+
+#define FLTFL_IO_OPERATION_NON_CACHED                0x00000001
+#define FLTFL_IO_OPERATION_PAGING                    0x00000002
+#define FLTFL_IO_OPERATION_DO_NOT_UPDATE_BYTE_OFFSET 0x00000004
+#define FLTFL_IO_OPERATION_SYNCHRONOUS_PAGING        0x00000008
+
+typedef struct _FLT_FILE_NAME_INFORMATION
+{
+    USHORT Size;
+    FLT_FILE_NAME_PARSED_FLAGS NamesParsed;
+    FLT_FILE_NAME_OPTIONS Format;
+    UNICODE_STRING Name;
+    UNICODE_STRING Volume;
+    UNICODE_STRING Share;
+    UNICODE_STRING Extension;
+    UNICODE_STRING Stream;
+    UNICODE_STRING FinalComponent;
+    UNICODE_STRING ParentDir;
+} FLT_FILE_NAME_INFORMATION, *PFLT_FILE_NAME_INFORMATION;
+
+NTSTATUS WINAPI FltAllocateContext(PFLT_FILTER, FLT_CONTEXT_TYPE, SIZE_T, POOL_TYPE, PFLT_CONTEXT*);
 NTSTATUS WINAPI FltBuildDefaultSecurityDescriptor(PSECURITY_DESCRIPTOR *, ACCESS_MASK);
+void     WINAPI FltCloseCommunicationPort (PFLT_PORT);
+NTSTATUS WINAPI FltCreateCommunicationPort(PFLT_FILTER, PFLT_PORT*, POBJECT_ATTRIBUTES, PVOID, PFLT_CONNECT_NOTIFY,
+                                            PFLT_DISCONNECT_NOTIFY, PFLT_MESSAGE_NOTIFY, LONG);
+NTSTATUS WINAPI FltEnumerateVolumes (PFLT_FILTER, PFLT_VOLUME *, ULONG, PULONG);
 void     WINAPI FltFreeSecurityDescriptor(PSECURITY_DESCRIPTOR);
+NTSTATUS WINAPI FltGetFileNameInformation(PFLT_CALLBACK_DATA, FLT_FILE_NAME_OPTIONS, PFLT_FILE_NAME_INFORMATION *);
+NTSTATUS WINAPI FltGetFileNameInformationUnsafe(PFILE_OBJECT, PFLT_INSTANCE, FLT_FILE_NAME_OPTIONS, PFLT_FILE_NAME_INFORMATION *);
 void*    WINAPI FltGetRoutineAddress(LPCSTR name);
+NTSTATUS WINAPI FltGetStreamContext(PFLT_INSTANCE, PFILE_OBJECT, PFLT_CONTEXT *);
+NTSTATUS WINAPI FltQueryInformationFile(PFLT_INSTANCE, PFILE_OBJECT, PVOID, ULONG, FILE_INFORMATION_CLASS, PULONG);
+NTSTATUS WINAPI FltParseFileNameInformation(PFLT_FILE_NAME_INFORMATION);
+NTSTATUS WINAPI FltReadFile(PFLT_INSTANCE, PFILE_OBJECT, PLARGE_INTEGER, ULONG, PVOID, FLT_IO_OPERATION_FLAGS,
+                            PULONG, PFLT_COMPLETED_ASYNC_IO_CALLBACK, PVOID);
 NTSTATUS WINAPI FltRegisterFilter(PDRIVER_OBJECT, const FLT_REGISTRATION *, PFLT_FILTER *);
+VOID     WINAPI FltReleaseContext(PFLT_CONTEXT);
+void     WINAPI FltReleaseFileNameInformation(PFLT_FILE_NAME_INFORMATION);
+NTSTATUS WINAPI FltSetStreamContext(PFLT_INSTANCE, PFILE_OBJECT, FLT_SET_CONTEXT_OPERATION, PFLT_CONTEXT, PFLT_CONTEXT*);
 NTSTATUS WINAPI FltStartFiltering(PFLT_FILTER);
 void     WINAPI FltUnregisterFilter(PFLT_FILTER);
 
diff --git a/include/fileapi.h b/include/fileapi.h
index 04bbfea60c21..06e288e422e4 100644
--- a/include/fileapi.h
+++ b/include/fileapi.h
@@ -34,8 +34,10 @@ typedef struct _CREATEFILE2_EXTENDED_PARAMETERS {
 
 WINBASEAPI HANDLE WINAPI CreateFile2(LPCWSTR,DWORD,DWORD,DWORD,LPCREATEFILE2_EXTENDED_PARAMETERS);
 WINBASEAPI HANDLE WINAPI FindFirstFileNameW(const WCHAR*,DWORD,DWORD*,WCHAR*);
-WINBASEAPI DWORD WINAPI GetTempPath2A(DWORD,LPSTR);
-WINBASEAPI DWORD WINAPI GetTempPath2W(DWORD,LPWSTR);
+WINBASEAPI BOOL   WINAPI FindNextFileNameW(HANDLE,DWORD*,WCHAR*);
+WINBASEAPI DWORD  WINAPI GetTempPath2A(DWORD,LPSTR);
+WINBASEAPI DWORD  WINAPI GetTempPath2W(DWORD,LPWSTR);
+#define                  GetTempPath2 WINELIB_NAME_AW(GetTempPath2)
 
 #ifdef __cplusplus
 }
diff --git a/include/ntuser.h b/include/ntuser.h
index c3c4c8cdcf2f..be41b33adfd7 100644
--- a/include/ntuser.h
+++ b/include/ntuser.h
@@ -1600,11 +1600,22 @@ struct hid_packet
 
 C_ASSERT(sizeof(struct hid_packet) == offsetof(struct hid_packet, data[0]));
 
+struct raw_mouse
+{
+    UINT  count;
+    POINT data[64]; /* arbitrary buffer size */
+};
+
 struct send_hardware_input_params
 {
     UINT flags;
     const INPUT *input;
-    LPARAM lparam;  /* struct hid_packet pointer for WM_INPUT* messages */
+    union
+    {
+        LPARAM             lparam;
+        struct hid_packet *hid_packet; /* WM_INPUT* */
+        struct raw_mouse  *raw_mouse;  /* WM_MOUSEMOVE* */
+    };
 };
 
 static inline BOOL NtUserSendHardwareInput( HWND hwnd, UINT flags, const INPUT *input, LPARAM lparam )
@@ -1618,12 +1629,11 @@ struct expose_window_surface_params
     UINT flags;
     BOOL whole;
     RECT rect;
-    UINT dpi;
 };
 
-static inline BOOL NtUserExposeWindowSurface( HWND hwnd, UINT flags, const RECT *rect, UINT dpi )
+static inline BOOL NtUserExposeWindowSurface( HWND hwnd, UINT flags, const RECT *rect )
 {
-    struct expose_window_surface_params params = {.flags = flags, .whole = !rect, .dpi = dpi};
+    struct expose_window_surface_params params = {.flags = flags, .whole = !rect};
     if (rect) params.rect = *rect;
     return NtUserCallHwndParam( hwnd, (UINT_PTR)&params, NtUserCallHwndParam_ExposeWindowSurface );
 }
diff --git a/include/processsnapshot.h b/include/processsnapshot.h
new file mode 100644
index 000000000000..91338dea1533
--- /dev/null
+++ b/include/processsnapshot.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2025 Louis Lenders
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef _PROCESSSNAPSHOT_H_
+#define _PROCESSSNAPSHOT_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum PSS_CAPTURE_FLAGS
+{
+    PSS_CAPTURE_NONE                             = 0x00000000,
+    PSS_CAPTURE_VA_CLONE                         = 0x00000001,
+    PSS_CAPTURE_RESERVED_00000002                = 0x00000002,
+    PSS_CAPTURE_HANDLES                          = 0x00000004,
+    PSS_CAPTURE_HANDLE_NAME_INFORMATION          = 0x00000008,
+    PSS_CAPTURE_HANDLE_BASIC_INFORMATION         = 0x00000010,
+    PSS_CAPTURE_HANDLE_TYPE_SPECIFIC_INFORMATION = 0x00000020,
+    PSS_CAPTURE_HANDLE_TRACE                     = 0x00000040,
+    PSS_CAPTURE_THREADS                          = 0x00000080,
+    PSS_CAPTURE_THREAD_CONTEXT                   = 0x00000100,
+    PSS_CAPTURE_THREAD_CONTEXT_EXTENDED          = 0x00000200,
+    PSS_CAPTURE_RESERVED_00000400                = 0x00000400,
+    PSS_CAPTURE_VA_SPACE                         = 0x00000800,
+    PSS_CAPTURE_VA_SPACE_SECTION_INFORMAN        = 0x00001000,
+    PSS_CAPTURE_IPT_TRACE                        = 0x00002000,
+    PSS_CAPTURE_RESERVED_00004000                = 0x00004000,
+    PSS_CREATE_BREAKAWAY_OPTIONAL                = 0x04000000,
+    PSS_CREATE_BREAKAWAY                         = 0x08000000,
+    PSS_CREATE_FORCE_BREAKAWAY                   = 0x10000000,
+    PSS_CREATE_USE_VM_ALLOCATIONS                = 0x20000000,
+    PSS_CREATE_MEASURE_PERFORMANCE               = 0x40000000,
+    PSS_CREATE_RELEASE_SECTION                   = 0x80000000
+} PSS_CAPTURE_FLAGS;
+
+typedef enum PSS_QUERY_INFORMATION_CLASS
+{
+    PSS_QUERY_PROCESS_INFORMATION         = 0,
+    PSS_QUERY_VA_CLONE_INFORMATION        = 1,
+    PSS_QUERY_AUXILIARY_PAGES_INFORMATION = 2,
+    PSS_QUERY_VA_SPACE_INFORMATION        = 3,
+    PSS_QUERY_HANDLE_INFORMATION          = 4,
+    PSS_QUERY_THREAD_INFORMATION          = 5,
+    PSS_QUERY_HANDLE_TRACE_INFORMATION    = 6,
+    PSS_QUERY_PERFORMANCE_COUNTERS        = 7
+} PSS_QUERY_INFORMATION_CLASS;
+
+DECLARE_HANDLE(HPSS);
+
+DWORD WINAPI PssCaptureSnapshot(HANDLE,PSS_CAPTURE_FLAGS,DWORD,HPSS*);
+DWORD WINAPI PssFreeSnapshot(HANDLE,HPSS);
+DWORD WINAPI PssQuerySnapshot(HPSS,PSS_QUERY_INFORMATION_CLASS,void*,DWORD);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _PROCESSSNAPSHOT_H_ */
diff --git a/include/windns.h b/include/windns.h
index 87e8dad37dab..17e81a926e38 100644
--- a/include/windns.h
+++ b/include/windns.h
@@ -781,6 +781,32 @@ typedef struct _DNS_SERVICE_CANCEL
     void *reserved;
 } DNS_SERVICE_CANCEL, *PDNS_SERVICE_CANCEL;
 
+typedef struct _MDNS_QUERY_HANDLE
+{
+    WCHAR nameBuf[DNS_MAX_NAME_BUFFER_LENGTH];
+    WORD wType;
+    PVOID pSubscription;
+    PVOID pWnfCallbackParams;
+    ULONG stateNameData[2];
+} MDNS_QUERY_HANDLE, *PMDNS_QUERY_HANDLE;
+
+typedef void WINAPI MDNS_QUERY_CALLBACK(PVOID,PMDNS_QUERY_HANDLE,PDNS_QUERY_RESULT);
+typedef MDNS_QUERY_CALLBACK *PMDNS_QUERY_CALLBACK;
+
+typedef struct _MDNS_QUERY_REQUEST
+{
+    ULONG Version;
+    ULONG ulRefCount;
+    PCWSTR Query;
+    WORD QueryType;
+    ULONG64 QueryOptions;
+    ULONG InterfaceIndex;
+    PMDNS_QUERY_CALLBACK pQueryCallback;
+    PVOID pQueryContext;
+    BOOL fAnswerReceived;
+    ULONG ulResendCount;
+} MDNS_QUERY_REQUEST, *PMDNS_QUERY_REQUEST;
+
 DNS_STATUS WINAPI DnsAcquireContextHandle_A(DWORD,PVOID,PHANDLE);
 DNS_STATUS WINAPI DnsAcquireContextHandle_W(DWORD,PVOID,PHANDLE);
 #define DnsAcquireContextHandle WINELIB_NAME_AW(DnsAcquireContextHandle_)
@@ -800,6 +826,7 @@ DNS_STATUS WINAPI DnsQuery_UTF8(PCSTR,WORD,DWORD,PVOID,PDNS_RECORDA*,PVOID*);
 #define DnsQuery WINELIB_NAME_AW(DnsQuery_)
 DNS_STATUS WINAPI DnsQueryEx(DNS_QUERY_REQUEST*,DNS_QUERY_RESULT*,DNS_QUERY_CANCEL*);
 DNS_STATUS WINAPI DnsCancelQuery(DNS_QUERY_CANCEL*);
+BOOL WINAPI DnsGetCacheDataTable(PDNS_CACHE_ENTRY*);
 DNS_STATUS WINAPI DnsQueryConfig(DNS_CONFIG_TYPE,DWORD,PCWSTR,PVOID,PVOID,PDWORD);
 BOOL WINAPI DnsRecordCompare(PDNS_RECORD,PDNS_RECORD);
 PDNS_RECORD WINAPI DnsRecordCopyEx(PDNS_RECORD,DNS_CHARSET,DNS_CHARSET);
@@ -813,13 +840,14 @@ DNS_STATUS WINAPI DnsReplaceRecordSetW(PDNS_RECORDW,DWORD,HANDLE,PVOID,PVOID);
 DNS_STATUS WINAPI DnsReplaceRecordSetUTF8(PDNS_RECORDA,DWORD,HANDLE,PVOID,PVOID);
 #define DnsReplaceRecordSet WINELIB_NAME_AW(DnsReplaceRecordSet)
 DNS_STATUS WINAPI DnsServiceBrowse(PDNS_SERVICE_BROWSE_REQUEST, PDNS_SERVICE_CANCEL);
+DNS_STATUS WINAPI DnsStartMulticastQuery(PMDNS_QUERY_REQUEST,PMDNS_QUERY_HANDLE);
+DNS_STATUS WINAPI DnsStopMulticastQuery(PMDNS_QUERY_HANDLE);
 DNS_STATUS WINAPI DnsValidateName_A(PCSTR,DNS_NAME_FORMAT);
 DNS_STATUS WINAPI DnsValidateName_W(PCWSTR, DNS_NAME_FORMAT);
 DNS_STATUS WINAPI DnsValidateName_UTF8(PCSTR,DNS_NAME_FORMAT);
 #define DnsValidateName WINELIB_NAME_AW(DnsValidateName_)
 BOOL WINAPI DnsWriteQuestionToBuffer_W(PDNS_MESSAGE_BUFFER,PDWORD,PCWSTR,WORD,WORD,BOOL);
 BOOL WINAPI DnsWriteQuestionToBuffer_UTF8(PDNS_MESSAGE_BUFFER,PDWORD,PCSTR,WORD,WORD,BOOL);
-BOOL WINAPI DnsGetCacheDataTable(PDNS_CACHE_ENTRY*);
 
 #ifdef __cplusplus
 }
diff --git a/include/wine/gdi_driver.h b/include/wine/gdi_driver.h
index 630e035a5896..384fdcac18ee 100644
--- a/include/wine/gdi_driver.h
+++ b/include/wine/gdi_driver.h
@@ -265,7 +265,10 @@ struct client_surface
     LONG                               ref;            /* reference count */
     HWND                               hwnd;           /* window the surface was created for */
     LONG                               updated;        /* has been moved / resized / reparented */
+    HWND                               toplevel;       /* toplevel window of the surface */
     LONG                               offscreen;      /* client window is offscreen */
+    RECT                               virtual_rect;   /* virtual size and position in the toplevel ancestor */
+    RECT                               monitor_rect;   /* raw physical size and position in the toplevel ancestor */
 };
 
 W32KAPI void *client_surface_create( UINT size, const struct client_surface_funcs *funcs, HWND hwnd );
@@ -273,6 +276,7 @@ W32KAPI void client_surface_add_ref( struct client_surface *surface );
 W32KAPI void client_surface_release( struct client_surface *surface );
 W32KAPI void client_surface_present( struct client_surface *surface );
 W32KAPI void client_surface_update( struct client_surface *surface );
+W32KAPI void update_client_surfaces( HWND hwnd );
 W32KAPI void detach_client_surfaces( HWND hwnd );
 
 static inline const char *debugstr_client_surface( struct client_surface *surface )
diff --git a/include/wine/opengl_driver.h b/include/wine/opengl_driver.h
index 3c71db20fd25..6d1ff154332f 100644
--- a/include/wine/opengl_driver.h
+++ b/include/wine/opengl_driver.h
@@ -79,6 +79,7 @@ struct opengl_client_context
     GLint                       profile_mask;
     int                         major_version;
     int                         minor_version;
+    BOOL                        broken_sharing;                         /* context couldn't be shared (for macOS) */
     BOOLEAN                     extensions[GL_EXTENSION_COUNT];         /* exposed client extensions */
     UINT32                      extension_count;                        /* size of supported extensions */
     UINT16                      extension_array[GL_EXTENSION_COUNT];    /* array of supported extensions */
@@ -242,7 +243,7 @@ struct opengl_driver_funcs
     BOOL (*p_describe_pixel_format)(int,struct wgl_pixel_format*);
     void (*p_init_extensions)( struct opengl_funcs *funcs, BOOLEAN extensions[GL_EXTENSION_COUNT] );
     BOOL (*p_surface_create)( struct client_surface *client, int format, struct opengl_drawable **drawable );
-    BOOL (*p_context_create)( int format, void *share, const int *attribs, void **context );
+    BOOL (*p_context_create)( int format, void *share, const int *attribs, void **context, BOOL *shared );
     BOOL (*p_context_destroy)(void*);
     BOOL (*p_make_current)( struct opengl_drawable *draw, struct opengl_drawable *read, void *private );
     BOOL (*p_pbuffer_create)( HDC hdc, int format, BOOL largest, GLenum texture_format, GLenum texture_target,
diff --git a/include/wine/server_protocol.h b/include/wine/server_protocol.h
index c74b015747a0..efc1cbe76c84 100644
--- a/include/wine/server_protocol.h
+++ b/include/wine/server_protocol.h
@@ -251,6 +251,12 @@ struct property_data
     lparam_t       data;
 };
 
+struct ratio
+{
+    unsigned short num;
+    unsigned short den;
+};
+
 
 struct rectangle
 {
@@ -334,6 +340,7 @@ union hw_input
         unsigned int   flags;
         unsigned int   time;
         lparam_t       info;
+        unsigned int   raw_count;
     } mouse;
     struct
     {
@@ -891,7 +898,8 @@ struct monitor_info
     struct rectangle raw;
     struct rectangle virt;
     unsigned int     flags;
-    unsigned int     dpi;
+    struct ratio     dpi;
+    struct ratio     raw_dpi;
 };
 #define MONITOR_FLAG_PRIMARY  0x01
 #define MONITOR_FLAG_CLONE    0x02
@@ -1062,6 +1070,8 @@ typedef volatile struct
     unsigned int         fnid;
     unsigned int         ansi;
     int                  __pad;
+    struct ratio         dpi;
+    struct ratio         raw_dpi;
     data_size_t          private_size;
     data_size_t          extra_size;
     struct window_info   info;
@@ -3144,7 +3154,7 @@ struct send_hardware_message_request
     user_handle_t   win;
     union hw_input  input;
     unsigned int    flags;
-    /* VARARG(report,bytes); */
+    /* VARARG(extra,bytes); */
     char __pad_60[4];
 };
 struct send_hardware_message_reply
@@ -3158,6 +3168,7 @@ struct send_hardware_message_reply
     char __pad_28[4];
 };
 #define SEND_HWMSG_INJECTED    0x01
+#define SEND_HWMSG_RAWINPUT    0x02
 
 
 
@@ -3703,7 +3714,7 @@ struct get_window_children_from_point_request
     user_handle_t  parent;
     int            x;
     int            y;
-    int            dpi;
+    struct ratio   dpi;
     char __pad_28[4];
 };
 struct get_window_children_from_point_reply
@@ -3740,13 +3751,11 @@ struct set_window_pos_request
     struct request_header __header;
     unsigned short swp_flags;
     unsigned short paint_flags;
-    unsigned int   monitor_dpi;
     user_handle_t  handle;
     user_handle_t  previous;
     struct rectangle window;
     struct rectangle client;
     /* VARARG(valid,rectangles); */
-    char __pad_60[4];
 };
 struct set_window_pos_reply
 {
@@ -3766,7 +3775,7 @@ struct get_window_rectangles_request
     struct request_header __header;
     user_handle_t  handle;
     int            relative;
-    int            dpi;
+    struct ratio   dpi;
 };
 struct get_window_rectangles_reply
 {
@@ -3817,7 +3826,7 @@ struct get_windows_offset_request
     struct request_header __header;
     user_handle_t  from;
     user_handle_t  to;
-    int            dpi;
+    struct ratio   dpi;
 };
 struct get_windows_offset_reply
 {
@@ -7149,6 +7158,6 @@ union generic_reply
     struct d3dkmt_mutex_release_reply d3dkmt_mutex_release_reply;
 };
 
-#define SERVER_PROTOCOL_VERSION 952
+#define SERVER_PROTOCOL_VERSION 956
 
 #endif /* __WINE_WINE_SERVER_PROTOCOL_H */
diff --git a/include/wine/wgl.h b/include/wine/wgl.h
index 3b46ee51b87d..36df3a2d5eec 100644
--- a/include/wine/wgl.h
+++ b/include/wine/wgl.h
@@ -7880,7 +7880,6 @@ typedef void       (GLAPIENTRY *PFN_glBufferParameteriAPPLE)( GLenum target, GLe
 typedef GLuint     (GLAPIENTRY *PFN_glBufferRegionEnabled)(void);
 typedef void       (GLAPIENTRY *PFN_glBufferStorage)( GLenum target, GLsizeiptr size, const void *data, GLbitfield flags );
 typedef void       (GLAPIENTRY *PFN_glBufferStorageEXT)( GLenum target, GLsizeiptr size, const void *data, GLbitfield flags );
-typedef void       (GLAPIENTRY *PFN_glBufferStorageExternalEXT)( GLenum target, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags );
 typedef void       (GLAPIENTRY *PFN_glBufferStorageMemEXT)( GLenum target, GLsizeiptr size, GLuint memory, GLuint64 offset );
 typedef void       (GLAPIENTRY *PFN_glBufferSubData)( GLenum target, GLintptr offset, GLsizeiptr size, const void *data );
 typedef void       (GLAPIENTRY *PFN_glBufferSubDataARB)( GLenum target, GLintptrARB offset, GLsizeiptrARB size, const void *data );
@@ -9353,7 +9352,6 @@ typedef void       (GLAPIENTRY *PFN_glNamedBufferPageCommitmentEXT)( GLuint buff
 typedef void       (GLAPIENTRY *PFN_glNamedBufferPageCommitmentMemNV)( GLuint buffer, GLintptr offset, GLsizeiptr size, GLuint memory, GLuint64 memOffset, GLboolean commit );
 typedef void       (GLAPIENTRY *PFN_glNamedBufferStorage)( GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags );
 typedef void       (GLAPIENTRY *PFN_glNamedBufferStorageEXT)( GLuint buffer, GLsizeiptr size, const void *data, GLbitfield flags );
-typedef void       (GLAPIENTRY *PFN_glNamedBufferStorageExternalEXT)( GLuint buffer, GLintptr offset, GLsizeiptr size, GLeglClientBufferEXT clientBuffer, GLbitfield flags );
 typedef void       (GLAPIENTRY *PFN_glNamedBufferStorageMemEXT)( GLuint buffer, GLsizeiptr size, GLuint memory, GLuint64 offset );
 typedef void       (GLAPIENTRY *PFN_glNamedBufferSubData)( GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data );
 typedef void       (GLAPIENTRY *PFN_glNamedBufferSubDataEXT)( GLuint buffer, GLintptr offset, GLsizeiptr size, const void *data );
@@ -11444,7 +11442,6 @@ typedef BOOL       (GLAPIENTRY *PFN_wglSwapIntervalEXT)( int interval );
     USE_GL_EXT(GL_EXT_draw_buffers2) \
     USE_GL_EXT(GL_EXT_draw_instanced) \
     USE_GL_EXT(GL_EXT_draw_range_elements) \
-    USE_GL_EXT(GL_EXT_external_buffer) \
     USE_GL_EXT(GL_EXT_float_blend) \
     USE_GL_EXT(GL_EXT_fog_coord) \
     USE_GL_EXT(GL_EXT_frag_depth) \
@@ -12434,7 +12431,6 @@ typedef BOOL       (GLAPIENTRY *PFN_wglSwapIntervalEXT)( int interval );
     USE_GL_FUNC(glBufferRegionEnabled) \
     USE_GL_FUNC(glBufferStorage) \
     USE_GL_FUNC(glBufferStorageEXT) \
-    USE_GL_FUNC(glBufferStorageExternalEXT) \
     USE_GL_FUNC(glBufferStorageMemEXT) \
     USE_GL_FUNC(glBufferSubData) \
     USE_GL_FUNC(glBufferSubDataARB) \
@@ -13907,7 +13903,6 @@ typedef BOOL       (GLAPIENTRY *PFN_wglSwapIntervalEXT)( int interval );
     USE_GL_FUNC(glNamedBufferPageCommitmentMemNV) \
     USE_GL_FUNC(glNamedBufferStorage) \
     USE_GL_FUNC(glNamedBufferStorageEXT) \
-    USE_GL_FUNC(glNamedBufferStorageExternalEXT) \
     USE_GL_FUNC(glNamedBufferStorageMemEXT) \
     USE_GL_FUNC(glNamedBufferSubData) \
     USE_GL_FUNC(glNamedBufferSubDataEXT) \
diff --git a/include/wine/wined3d.h b/include/wine/wined3d.h
index 355140043db5..002bab2b9c64 100644
--- a/include/wine/wined3d.h
+++ b/include/wine/wined3d.h
@@ -922,6 +922,7 @@ enum wined3d_memory_segment_group
 #define WINED3D_SWAPCHAIN_REGISTER_TOPMOST_TIMER                0x00100000u
 /* Allow the swapchain flag, but not actual locking */
 #define WINED3D_SWAPCHAIN_ALLOW_MS_LOCKABLE_BACKBUFFER          0x00200000u
+#define WINED3D_SWAPCHAIN_FRAME_LATENCY_WAITABLE_OBJECT         0x00400000u
 
 #define WINED3DDP_MAXTEXCOORD                                   8
 
@@ -2903,6 +2904,7 @@ struct wined3d_texture * __cdecl wined3d_swapchain_get_back_buffer(const struct
 struct wined3d_device * __cdecl wined3d_swapchain_get_device(const struct wined3d_swapchain *swapchain);
 HRESULT __cdecl wined3d_swapchain_get_display_mode(const struct wined3d_swapchain *swapchain,
         struct wined3d_display_mode *mode, enum wined3d_display_rotation *rotation);
+HANDLE __cdecl wined3d_swapchain_get_frame_latency_waitable_object(struct wined3d_swapchain *swapchain);
 struct wined3d_texture * __cdecl wined3d_swapchain_get_front_buffer(const struct wined3d_swapchain *swapchain);
 HRESULT __cdecl wined3d_swapchain_get_front_buffer_data(const struct wined3d_swapchain *swapchain,
         struct wined3d_texture *dst_texture, unsigned int sub_resource_idx);
@@ -2911,6 +2913,7 @@ HRESULT __cdecl wined3d_swapchain_get_gamma_ramp(const struct wined3d_swapchain
 void * __cdecl wined3d_swapchain_get_parent(const struct wined3d_swapchain *swapchain);
 void __cdecl wined3d_swapchain_get_desc(const struct wined3d_swapchain *swapchain,
         struct wined3d_swapchain_desc *desc);
+HRESULT __cdecl wined3d_swapchain_get_max_frame_latency(struct wined3d_swapchain *swapchain, unsigned int *latency);
 HRESULT __cdecl wined3d_swapchain_get_raster_status(const struct wined3d_swapchain *swapchain,
         struct wined3d_raster_status *raster_status);
 struct wined3d_swapchain_state * __cdecl wined3d_swapchain_get_state(struct wined3d_swapchain *swapchain);
@@ -2922,6 +2925,7 @@ HRESULT __cdecl wined3d_swapchain_resize_buffers(struct wined3d_swapchain *swapc
         enum wined3d_multisample_type multisample_type, unsigned int multisample_quality);
 HRESULT __cdecl wined3d_swapchain_set_gamma_ramp(const struct wined3d_swapchain *swapchain,
         uint32_t flags, const struct wined3d_gamma_ramp *ramp);
+HRESULT __cdecl wined3d_swapchain_set_max_frame_latency(struct wined3d_swapchain *swapchain, unsigned int latency);
 void __cdecl wined3d_swapchain_set_palette(struct wined3d_swapchain *swapchain, struct wined3d_palette *palette);
 void __cdecl wined3d_swapchain_set_window(struct wined3d_swapchain *swapchain, HWND window);
 
diff --git a/include/ws2spi.h b/include/ws2spi.h
index f2d21068a4f2..0eef1cdc76ad 100644
--- a/include/ws2spi.h
+++ b/include/ws2spi.h
@@ -45,6 +45,7 @@ INT      WINAPI WSCInstallProvider(const LPGUID,LPCWSTR,const LPWSAPROTOCOL_INFO
 INT      WINAPI WSCDeinstallProvider(LPGUID,LPINT);
 INT      WINAPI WSCEnableNSProvider(LPGUID,BOOL);
 INT      WINAPI WSCEnumProtocols(LPINT,LPWSAPROTOCOL_INFOW,LPDWORD,LPINT);
+INT      WINAPI WSCEnumProtocols32(LPINT,LPWSAPROTOCOL_INFOW,LPDWORD,LPINT);
 INT      WINAPI WSCGetApplicationCategory(LPCWSTR,DWORD,LPCWSTR,DWORD,DWORD*,LPINT);
 INT      WINAPI WSCGetProviderInfo(LPGUID,WSC_PROVIDER_INFO_TYPE,PBYTE,size_t*,DWORD,LPINT);
 INT      WINAPI WSCGetProviderPath(LPGUID,LPWSTR,LPINT,LPINT);
diff --git a/libs/faudio/src/FAudio_internal.c b/libs/faudio/src/FAudio_internal.c
index 83fce9c399f0..b43e80e14bbf 100644
--- a/libs/faudio/src/FAudio_internal.c
+++ b/libs/faudio/src/FAudio_internal.c
@@ -1918,7 +1918,7 @@ static void decode_mono_adpcm_block(const uint8_t *src, float *dst, uint32_t off
 		if (i == 0) *dst++ = sample2 / 32768.0;
 		if (i == 1) *dst++ = sample1 / 32768.0;
 	}
-	for (; i < offset + count; i += 2)
+	while (i < offset + count)
 	{
 		float high, low;
 
@@ -1929,6 +1929,12 @@ static void decode_mono_adpcm_block(const uint8_t *src, float *dst, uint32_t off
 			&sample1,
 			&sample2
 		);
+		if (i >= offset)
+			*dst++ = high;
+		i++;
+		if (i >= offset + count)
+			break;
+
 		low = FAudio_INTERNAL_ParseNibble(
 			*src & 0xf,
 			predictor,
@@ -1936,13 +1942,9 @@ static void decode_mono_adpcm_block(const uint8_t *src, float *dst, uint32_t off
 			&sample1,
 			&sample2
 		);
-
 		if (i >= offset)
-		{
-			*dst++ = high;
-			if (i + 1 < offset + count)
-				*dst++ = low;
-		}
+			*dst++ = low;
+		i++;
 
 		++src;
 	}
diff --git a/libs/ffmpeg/Makefile.in b/libs/ffmpeg/Makefile.in
index 35ccba4157a6..8e45067f8e82 100644
--- a/libs/ffmpeg/Makefile.in
+++ b/libs/ffmpeg/Makefile.in
@@ -19,6 +19,8 @@ SOURCES = \
 	libavutil/arm/float_dsp_init_vfp.c \
 	libavutil/arm/float_dsp_neon.S \
 	libavutil/arm/float_dsp_vfp.S \
+	libavutil/arm64ec/cpu.c \
+	libavutil/arm64ec/crc.S \
 	libavutil/audio_fifo.c \
 	libavutil/avsscanf.c \
 	libavutil/avstring.c \
@@ -127,6 +129,10 @@ SOURCES = \
 	libswresample/arm/audio_convert_neon.S \
 	libswresample/arm/resample.S \
 	libswresample/arm/resample_init.c \
+	libswresample/arm64ec/audio_convert_init.c \
+	libswresample/arm64ec/audio_convert_neon.S \
+	libswresample/arm64ec/resample.S \
+	libswresample/arm64ec/resample_init.c \
 	libswresample/audioconvert.c \
 	libswresample/dither.c \
 	libswresample/options.c \
@@ -159,6 +165,16 @@ SOURCES = \
 	libswscale/arm/swscale.c \
 	libswscale/arm/swscale_unscaled.c \
 	libswscale/arm/yuv2rgb_neon.S \
+	libswscale/arm64ec/hscale.S \
+	libswscale/arm64ec/input.S \
+	libswscale/arm64ec/output.S \
+	libswscale/arm64ec/range_convert_neon.S \
+	libswscale/arm64ec/rgb2rgb.c \
+	libswscale/arm64ec/rgb2rgb_neon.S \
+	libswscale/arm64ec/swscale.c \
+	libswscale/arm64ec/swscale_unscaled.c \
+	libswscale/arm64ec/swscale_unscaled_neon.S \
+	libswscale/arm64ec/yuv2rgb_neon.S \
 	libswscale/cms.c \
 	libswscale/csputils.c \
 	libswscale/format.c \
diff --git a/libs/ffmpeg/config.h b/libs/ffmpeg/config.h
index 49178cc252e3..8ada4da2cf42 100644
--- a/libs/ffmpeg/config.h
+++ b/libs/ffmpeg/config.h
@@ -833,7 +833,7 @@
 
 /* tweaks for Wine build */
 
-#if defined __aarch64__
+#if defined __aarch64__ || defined __arm64ec__
 
 #undef  ARCH_AARCH64
 #define ARCH_AARCH64 1
@@ -844,10 +844,6 @@
 #undef  CONFIG_PIC
 #define CONFIG_PIC 1
 
-#elif defined __arm64ec__
-
-/* FIXME */
-
 #elif defined __arm__
 
 #undef  ARCH_ARM
diff --git a/libs/ffmpeg/libavutil/aarch64/asm.S b/libs/ffmpeg/libavutil/aarch64/asm.S
index f0dadd23d92e..7fff6dde10d7 100644
--- a/libs/ffmpeg/libavutil/aarch64/asm.S
+++ b/libs/ffmpeg/libavutil/aarch64/asm.S
@@ -262,6 +262,22 @@ DISABLE_SME2
         .popsection
 #endif
 
+#ifdef __arm64ec__
+.macro  function name, export=0, align=4
+    .macro endfunc
+        .purgem endfunc
+    .endm
+        .text
+        .align          \align
+    .if \export
+        .global "#\name"
+"#\name":
+        AARCH64_VALID_CALL_TARGET
+    .else
+"#\name":
+    .endif
+.endm
+#else
 .macro  function name, export=0, align=4
     .macro endfunc
 ELF     .size   \name, . - \name
@@ -282,6 +298,7 @@ FUNC    .func   \name
 \name:
     .endif
 .endm
+#endif
 
 .macro  const   name, align=4, relocate=0
     .macro endconst
diff --git a/libs/ffmpeg/libavutil/arm64ec/cpu.c b/libs/ffmpeg/libavutil/arm64ec/cpu.c
new file mode 100644
index 000000000000..36746d14280a
--- /dev/null
+++ b/libs/ffmpeg/libavutil/arm64ec/cpu.c
@@ -0,0 +1 @@
+#include "../aarch64/cpu.c"
diff --git a/libs/ffmpeg/libavutil/arm64ec/crc.S b/libs/ffmpeg/libavutil/arm64ec/crc.S
new file mode 100644
index 000000000000..0f849f59d18b
--- /dev/null
+++ b/libs/ffmpeg/libavutil/arm64ec/crc.S
@@ -0,0 +1 @@
+#include "../aarch64/crc.S"
diff --git a/libs/ffmpeg/libswresample/arm64ec/audio_convert_init.c b/libs/ffmpeg/libswresample/arm64ec/audio_convert_init.c
new file mode 100644
index 000000000000..9b0495bfaebe
--- /dev/null
+++ b/libs/ffmpeg/libswresample/arm64ec/audio_convert_init.c
@@ -0,0 +1 @@
+#include "../aarch64/audio_convert_init.c"
diff --git a/libs/ffmpeg/libswresample/arm64ec/audio_convert_neon.S b/libs/ffmpeg/libswresample/arm64ec/audio_convert_neon.S
new file mode 100644
index 000000000000..d61ebb7a1f14
--- /dev/null
+++ b/libs/ffmpeg/libswresample/arm64ec/audio_convert_neon.S
@@ -0,0 +1,377 @@
+/*
+ * Copyright (c) 2008 Mans Rullgard <mans@mansr.com>
+ * Copyright (c) 2014 Janne Grunau <janne-libav@jannau.net>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "config.h"
+#include "libavutil/aarch64/asm.S"
+
+function swri_oldapi_conv_flt_to_s16_neon, export=1
+oldapi_conv_flt_to_s16_neon:
+        subs            x2,  x2,  #8
+        ld1             {v0.4s}, [x1],  #16
+        fcvtzs          v4.4s,  v0.4s,  #31
+        ld1             {v1.4s}, [x1],  #16
+        fcvtzs          v5.4s,  v1.4s,  #31
+        b.eq            3f
+        ands            x12, x2,  #~15
+        b.eq            2f
+1:      subs            x12, x12, #16
+        sqrshrn         v4.4h,  v4.4s,  #16
+        ld1             {v2.4s}, [x1],  #16
+        fcvtzs          v6.4s,  v2.4s,  #31
+        sqrshrn2        v4.8h,  v5.4s,  #16
+        ld1             {v3.4s}, [x1],  #16
+        fcvtzs          v7.4s,  v3.4s,  #31
+        sqrshrn         v6.4h,  v6.4s,  #16
+        st1             {v4.8h}, [x0],  #16
+        sqrshrn2        v6.8h,  v7.4s,  #16
+        ld1             {v0.4s}, [x1],  #16
+        fcvtzs          v4.4s,  v0.4s,  #31
+        ld1             {v1.4s}, [x1],  #16
+        fcvtzs          v5.4s,  v1.4s,  #31
+        st1             {v6.8h}, [x0],  #16
+        b.ne            1b
+        ands            x2,  x2,  #15
+        b.eq            3f
+2:      ld1             {v2.4s}, [x1],  #16
+        sqrshrn         v4.4h,  v4.4s,  #16
+        fcvtzs          v6.4s,  v2.4s,  #31
+        ld1             {v3.4s}, [x1],  #16
+        sqrshrn2        v4.8h,  v5.4s,  #16
+        fcvtzs          v7.4s,  v3.4s,  #31
+        sqrshrn         v6.4h,  v6.4s,  #16
+        st1             {v4.8h}, [x0],  #16
+        sqrshrn2        v6.8h,  v7.4s,  #16
+        st1             {v6.8h}, [x0]
+        ret
+3:      sqrshrn         v4.4h,  v4.4s,  #16
+        sqrshrn2        v4.8h,  v5.4s,  #16
+        st1             {v4.8h}, [x0]
+        ret
+endfunc
+
+function swri_oldapi_conv_fltp_to_s16_2ch_neon, export=1
+oldapi_conv_fltp_to_s16_2ch_neon:
+        stp             d8, d9, [sp, #-64]!
+        stp             d10, d11, [sp, #16]
+        stp             d12, d13, [sp, #32]
+        stp             d14, d15, [sp, #48]
+        ldp             x4,  x5,  [x1]
+        subs            x2,  x2,  #8
+        ld1             {v0.4s},  [x4], #16
+        fcvtzs          v4.4s,  v0.4s,  #31
+        ld1             {v1.4s},  [x4], #16
+        fcvtzs          v5.4s,  v1.4s,  #31
+        ld1             {v2.4s},  [x5], #16
+        fcvtzs          v6.4s,  v2.4s,  #31
+        ld1             {v3.4s},  [x5], #16
+        fcvtzs          v7.4s,  v3.4s,  #31
+        b.eq            3f
+        ands            x12, x2,  #~15
+        b.eq            2f
+1:      subs            x12, x12, #16
+        ld1             {v8.4s}, [x4], #16
+        fcvtzs          v12.4s, v8.4s, #31
+        sri             v6.4s,  v4.4s,  #16
+        ld1             {v9.4s}, [x4], #16
+        fcvtzs          v13.4s, v9.4s, #31
+        ld1             {v10.4s}, [x5], #16
+        fcvtzs          v14.4s, v10.4s, #31
+        ld1             {v11.4s}, [x5], #16
+        sri             v7.4s,  v5.4s,  #16
+        st1             {v6.4s},  [x0], #16
+        fcvtzs          v15.4s, v11.4s, #31
+        st1             {v7.4s},  [x0], #16
+        sri             v14.4s, v12.4s, #16
+        ld1             {v0.4s},  [x4], #16
+        sri             v15.4s, v13.4s, #16
+        st1             {v14.4s}, [x0], #16
+        fcvtzs          v4.4s,  v0.4s,  #31
+        ld1             {v1.4s},  [x4], #16
+        fcvtzs          v5.4s,  v1.4s,  #31
+        ld1             {v2.4s},  [x5], #16
+        fcvtzs          v6.4s,  v2.4s,  #31
+        ld1             {v3.4s},  [x5], #16
+        fcvtzs          v7.4s,  v3.4s,  #31
+        st1             {v15.4s}, [x0], #16
+        b.ne            1b
+        ands            x2,  x2,  #15
+        b.eq            3f
+2:      sri             v6.4s,  v4.4s,  #16
+        ld1             {v0.4s},  [x4], #16
+        fcvtzs          v0.4s,  v0.4s,  #31
+        ld1             {v1.4s},  [x4], #16
+        fcvtzs          v1.4s,  v1.4s,  #31
+        ld1             {v2.4s},  [x5], #16
+        fcvtzs          v2.4s,  v2.4s,  #31
+        sri             v7.4s,  v5.4s,  #16
+        ld1             {v3.4s},  [x5], #16
+        fcvtzs          v3.4s,  v3.4s,  #31
+        sri             v2.4s,  v0.4s,  #16
+        st1             {v6.4s,v7.4s},  [x0], #32
+        sri             v3.4s,  v1.4s,  #16
+        st1             {v2.4s,v3.4s},  [x0], #32
+        b               4f
+3:      sri             v6.4s,  v4.4s,  #16
+        sri             v7.4s,  v5.4s,  #16
+        st1             {v6.4s,v7.4s},  [x0]
+4:      ldp             d10, d11, [sp, #16]
+        ldp             d12, d13, [sp, #32]
+        ldp             d14, d15, [sp, #48]
+        ldp             d8, d9, [sp], #64
+        ret
+endfunc
+
+function swri_oldapi_conv_fltp_to_s16_nch_neon, export=1
+        cmp             w3,  #2
+        b.eq            oldapi_conv_fltp_to_s16_2ch_neon
+        b.gt            1f
+        ldr             x1,  [x1]
+        b               oldapi_conv_flt_to_s16_neon
+1:
+        stp             d8, d9, [sp, #-32]!
+        stp             d10, d11, [sp, #16]
+        cmp             w3,  #4
+        lsl             x12, x3,  #1
+        b.lt            4f
+
+5:      // 4 channels
+        ldp             x4, x5, [x1], #16
+        ldp             x6, x7, [x1], #16
+        mov             w9,  w2
+        mov             x8,  x0
+        ld1             {v4.4s},        [x4], #16
+        fcvtzs          v4.4s,  v4.4s,  #31
+        ld1             {v5.4s},        [x5], #16
+        fcvtzs          v5.4s,  v5.4s,  #31
+        ld1             {v6.4s},        [x6], #16
+        fcvtzs          v6.4s, v6.4s, #31
+        ld1             {v7.4s},        [x7], #16
+        fcvtzs          v7.4s, v7.4s, #31
+6:
+        subs            w9,  w9,  #8
+        ld1             {v0.4s},        [x4], #16
+        fcvtzs          v0.4s,  v0.4s,  #31
+        sri             v5.4s,  v4.4s,  #16
+        ld1             {v1.4s},        [x5], #16
+        fcvtzs          v1.4s,  v1.4s,  #31
+        sri             v7.4s,  v6.4s,  #16
+        ld1             {v2.4s},        [x6], #16
+        fcvtzs          v2.4s,  v2.4s,  #31
+        zip1            v8.4s, v5.4s,  v7.4s
+        ld1             {v3.4s},        [x7], #16
+        fcvtzs          v3.4s,  v3.4s,  #31
+        zip2            v9.4s, v5.4s,  v7.4s
+        st1             {v8.d}[0],     [x8], x12
+        sri             v1.4s,  v0.4s,  #16
+        st1             {v8.d}[1],     [x8], x12
+        sri             v3.4s,  v2.4s,  #16
+        st1             {v9.d}[0],     [x8], x12
+        zip1            v10.4s, v1.4s,  v3.4s
+        st1             {v9.d}[1],     [x8], x12
+        zip2            v11.4s, v1.4s,  v3.4s
+        b.eq            7f
+        ld1             {v4.4s},        [x4], #16
+        fcvtzs          v4.4s,  v4.4s,  #31
+        st1             {v10.d}[0],     [x8], x12
+        ld1             {v5.4s},        [x5], #16
+        fcvtzs          v5.4s,  v5.4s,  #31
+        st1             {v10.d}[1],     [x8], x12
+        ld1             {v6.4s},    [x6], #16
+        fcvtzs          v6.4s, v6.4s, #31
+        st1             {v11.d}[0],     [x8], x12
+        ld1             {v7.4s},    [x7], #16
+        fcvtzs          v7.4s, v7.4s, #31
+        st1             {v11.d}[1],     [x8], x12
+        b               6b
+7:
+        st1             {v10.d}[0],     [x8], x12
+        st1             {v10.d}[1],     [x8], x12
+        st1             {v11.d}[0],     [x8], x12
+        st1             {v11.d}[1],     [x8], x12
+        subs            w3,  w3,  #4
+        b.eq            end
+        cmp             w3,  #4
+        add             x0,  x0,  #8
+        b.ge            5b
+
+4:      // 2 channels
+        cmp             w3,  #2
+        b.lt            4f
+        ldp             x4,  x5,  [x1], #16
+        mov             w9,  w2
+        mov             x8,  x0
+        tst             w9,  #8
+        ld1             {v4.4s},        [x4], #16
+        fcvtzs          v4.4s,  v4.4s,  #31
+        ld1             {v5.4s},        [x5], #16
+        fcvtzs          v5.4s,  v5.4s,  #31
+        ld1             {v6.4s},        [x4], #16
+        fcvtzs          v6.4s,  v6.4s,  #31
+        ld1             {v7.4s},        [x5], #16
+        fcvtzs          v7.4s,  v7.4s,  #31
+        b.eq            6f
+        subs            w9,  w9,  #8
+        b.eq            7f
+        sri             v5.4s,  v4.4s,  #16
+        ld1             {v4.4s},        [x4], #16
+        fcvtzs          v4.4s,  v4.4s,  #31
+        st1             {v5.s}[0],      [x8], x12
+        sri             v7.4s,  v6.4s,  #16
+        st1             {v5.s}[1],      [x8], x12
+        ld1             {v6.4s},        [x4], #16
+        fcvtzs          v6.4s,  v6.4s, #31
+        st1             {v5.s}[2],      [x8], x12
+        st1             {v5.s}[3],      [x8], x12
+        st1             {v7.s}[0],      [x8], x12
+        st1             {v7.s}[1],      [x8], x12
+        ld1             {v5.4s},        [x5], #16
+        fcvtzs          v5.4s,  v5.4s,  #31
+        st1             {v7.s}[2],      [x8], x12
+        st1             {v7.s}[3],      [x8], x12
+        ld1             {v7.4s},        [x5], #16
+        fcvtzs          v7.4s,  v7.4s,  #31
+6:
+        subs            w9,  w9,  #16
+        ld1             {v0.4s},        [x4], #16
+        sri             v5.4s,  v4.4s,  #16
+        fcvtzs          v0.4s,  v0.4s,  #31
+        ld1             {v1.4s},        [x5], #16
+        sri             v7.4s,  v6.4s,  #16
+        st1             {v5.s}[0],      [x8], x12
+        st1             {v5.s}[1],      [x8], x12
+        fcvtzs          v1.4s,  v1.4s,  #31
+        st1             {v5.s}[2],      [x8], x12
+        st1             {v5.s}[3],      [x8], x12
+        ld1             {v2.4s},        [x4], #16
+        st1             {v7.s}[0],      [x8], x12
+        fcvtzs          v2.4s,  v2.4s,  #31
+        st1             {v7.s}[1],      [x8], x12
+        ld1             {v3.4s},        [x5], #16
+        st1             {v7.s}[2],      [x8], x12
+        fcvtzs          v3.4s,  v3.4s,  #31
+        st1             {v7.s}[3],      [x8], x12
+        sri             v1.4s,  v0.4s,  #16
+        sri             v3.4s,  v2.4s,  #16
+        b.eq            6f
+        ld1             {v4.4s},        [x4], #16
+        st1             {v1.s}[0],      [x8], x12
+        fcvtzs          v4.4s,  v4.4s,  #31
+        st1             {v1.s}[1],      [x8], x12
+        ld1             {v5.4s},        [x5], #16
+        st1             {v1.s}[2],      [x8], x12
+        fcvtzs          v5.4s,  v5.4s,  #31
+        st1             {v1.s}[3],      [x8], x12
+        ld1             {v6.4s},        [x4], #16
+        st1             {v3.s}[0],      [x8], x12
+        fcvtzs          v6.4s,  v6.4s,  #31
+        st1             {v3.s}[1],      [x8], x12
+        ld1             {v7.4s},        [x5], #16
+        st1             {v3.s}[2],      [x8], x12
+        fcvtzs          v7.4s,  v7.4s,  #31
+        st1             {v3.s}[3],      [x8], x12
+        b.gt            6b
+6:
+        st1             {v1.s}[0],      [x8], x12
+        st1             {v1.s}[1],      [x8], x12
+        st1             {v1.s}[2],      [x8], x12
+        st1             {v1.s}[3],      [x8], x12
+        st1             {v3.s}[0],      [x8], x12
+        st1             {v3.s}[1],      [x8], x12
+        st1             {v3.s}[2],      [x8], x12
+        st1             {v3.s}[3],      [x8], x12
+        b               8f
+7:
+        sri             v5.4s,  v4.4s,  #16
+        sri             v7.4s,  v6.4s,  #16
+        st1             {v5.s}[0],      [x8], x12
+        st1             {v5.s}[1],      [x8], x12
+        st1             {v5.s}[2],      [x8], x12
+        st1             {v5.s}[3],      [x8], x12
+        st1             {v7.s}[0],      [x8], x12
+        st1             {v7.s}[1],      [x8], x12
+        st1             {v7.s}[2],      [x8], x12
+        st1             {v7.s}[3],      [x8], x12
+8:
+        subs            w3,  w3,  #2
+        add             x0,  x0,  #4
+        b.eq            end
+
+4:      // 1 channel
+        ldr             x4,  [x1]
+        tst             w2,  #8
+        mov             w9,  w2
+        mov             x5,  x0
+        ld1             {v0.4s},        [x4], #16
+        fcvtzs          v0.4s,  v0.4s,  #31
+        ld1             {v1.4s},        [x4], #16
+        fcvtzs          v1.4s,  v1.4s,  #31
+        b.ne            8f
+6:
+        subs            w9,  w9,  #16
+        ld1             {v2.4s},        [x4], #16
+        fcvtzs          v2.4s,  v2.4s,  #31
+        ld1             {v3.4s},        [x4], #16
+        fcvtzs          v3.4s,  v3.4s,  #31
+        st1             {v0.h}[1],      [x5], x12
+        st1             {v0.h}[3],      [x5], x12
+        st1             {v0.h}[5],      [x5], x12
+        st1             {v0.h}[7],      [x5], x12
+        st1             {v1.h}[1],      [x5], x12
+        st1             {v1.h}[3],      [x5], x12
+        st1             {v1.h}[5],      [x5], x12
+        st1             {v1.h}[7],      [x5], x12
+        b.eq            7f
+        ld1             {v0.4s},        [x4], #16
+        fcvtzs          v0.4s,  v0.4s,  #31
+        ld1             {v1.4s},        [x4], #16
+        fcvtzs          v1.4s,  v1.4s,  #31
+7:
+        st1             {v2.h}[1],      [x5], x12
+        st1             {v2.h}[3],      [x5], x12
+        st1             {v2.h}[5],      [x5], x12
+        st1             {v2.h}[7],      [x5], x12
+        st1             {v3.h}[1],      [x5], x12
+        st1             {v3.h}[3],      [x5], x12
+        st1             {v3.h}[5],      [x5], x12
+        st1             {v3.h}[7],      [x5], x12
+        b.gt            6b
+        b               end
+8:
+        subs            w9,  w9,  #8
+        st1             {v0.h}[1],      [x5], x12
+        st1             {v0.h}[3],      [x5], x12
+        st1             {v0.h}[5],      [x5], x12
+        st1             {v0.h}[7],      [x5], x12
+        st1             {v1.h}[1],      [x5], x12
+        st1             {v1.h}[3],      [x5], x12
+        st1             {v1.h}[5],      [x5], x12
+        st1             {v1.h}[7],      [x5], x12
+        b.eq            end
+        ld1             {v0.4s},        [x4], #16
+        fcvtzs          v0.4s,  v0.4s,  #31
+        ld1             {v1.4s},        [x4], #16
+        fcvtzs          v1.4s,  v1.4s,  #31
+        b               6b
+end:
+        ldp             d10, d11, [sp, #16]
+        ldp             d8, d9, [sp], #32
+        ret
+endfunc
diff --git a/libs/ffmpeg/libswresample/arm64ec/resample.S b/libs/ffmpeg/libswresample/arm64ec/resample.S
new file mode 100644
index 000000000000..7126366871b5
--- /dev/null
+++ b/libs/ffmpeg/libswresample/arm64ec/resample.S
@@ -0,0 +1 @@
+#include "../aarch64/resample.S"
diff --git a/libs/ffmpeg/libswresample/arm64ec/resample_init.c b/libs/ffmpeg/libswresample/arm64ec/resample_init.c
new file mode 100644
index 000000000000..eafa08762eb4
--- /dev/null
+++ b/libs/ffmpeg/libswresample/arm64ec/resample_init.c
@@ -0,0 +1 @@
+#include "../aarch64/resample_init.c"
diff --git a/libs/ffmpeg/libswscale/aarch64/swscale.c b/libs/ffmpeg/libswscale/aarch64/swscale.c
index 4f86364f4bac..084634ae9525 100644
--- a/libs/ffmpeg/libswscale/aarch64/swscale.c
+++ b/libs/ffmpeg/libswscale/aarch64/swscale.c
@@ -39,6 +39,7 @@ static_assert(offsetof(SwsColorXform, mat) +
               2 * SIZEOF_MEMBER(SwsColorXform, mat[0][0]) == SCX_MAT_22,
               "struct layout mismatch");
 
+#ifndef __arm64ec__
 void ff_xyz12Torgb48le_neon_asm(const SwsColorXform *c, uint8_t *dst,
                                 int dst_stride, const uint8_t *src,
                                 int src_stride, int w, int h);
@@ -50,6 +51,7 @@ static void xyz12Torgb48le_neon(const SwsInternal *c, uint8_t *dst,
     ff_xyz12Torgb48le_neon_asm(&c->xyz2rgb, dst, dst_stride, src, src_stride,
                                w, h);
 }
+#endif
 
 void ff_hscale16to15_4_neon_asm(int shift, int16_t *_dst, int dstW,
                       const uint8_t *_src, const int16_t *filter,
@@ -342,7 +344,9 @@ av_cold void ff_sws_init_xyzdsp_aarch64(SwsInternal *c)
 
     if (have_neon(cpu_flags)) {
         if (!isBE(c->opts.src_format)) {
+#ifndef __arm64ec__
             c->xyz12Torgb48 = xyz12Torgb48le_neon;
+#endif
         }
     }
 }
diff --git a/libs/ffmpeg/libswscale/arm64ec/asm-offsets.h b/libs/ffmpeg/libswscale/arm64ec/asm-offsets.h
new file mode 100644
index 000000000000..ed95f5682bb9
--- /dev/null
+++ b/libs/ffmpeg/libswscale/arm64ec/asm-offsets.h
@@ -0,0 +1 @@
+#include "../aarch64/asm-offsets.h"
diff --git a/libs/ffmpeg/libswscale/arm64ec/hscale.S b/libs/ffmpeg/libswscale/arm64ec/hscale.S
new file mode 100644
index 000000000000..ec93e6373e1a
--- /dev/null
+++ b/libs/ffmpeg/libswscale/arm64ec/hscale.S
@@ -0,0 +1,1441 @@
+/*
+ * Copyright (c) 2016 Clément Bœsch <clement stupeflix.com>
+ * Copyright (c) 2019-2021 Sebastian Pop <spop@amazon.com>
+ * Copyright (c) 2022 Jonathan Swinney <jswinney@amazon.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/aarch64/asm.S"
+
+/*
+;-----------------------------------------------------------------------------
+; horizontal line scaling
+;
+; void hscale<source_width>to<intermediate_nbits>_<filterSize>_<opt>
+;                               (SwsInternal *c, int{16,32}_t *dst,
+;                                int dstW, const uint{8,16}_t *src,
+;                                const int16_t *filter,
+;                                const int32_t *filterPos, int filterSize);
+;
+; Scale one horizontal line. Input is either 8-bit width or 16-bit width
+; ($source_width can be either 8, 9, 10 or 16, difference is whether we have to
+; downscale before multiplying). Filter is 14 bits. Output is either 15 bits
+; (in int16_t) or 19 bits (in int32_t), as given in $intermediate_nbits. Each
+; output pixel is generated from $filterSize input pixels, the position of
+; the first pixel is given in filterPos[nOutputPixel].
+;----------------------------------------------------------------------------- */
+
+function ff_hscale8to15_X8_neon, export=1
+        sbfiz           x7, x6, #1, #32             // filterSize*2 (*2 because int16)
+1:      ldr             w8, [x5], #4                // filterPos[idx]
+        ldr             w0, [x5], #4                // filterPos[idx + 1]
+        ldr             w11, [x5], #4               // filterPos[idx + 2]
+        ldr             w9, [x5], #4                // filterPos[idx + 3]
+        mov             x16, x4                     // filter0 = filter
+        add             x12, x16, x7                // filter1 = filter0 + filterSize*2
+        add             x10, x12, x7                // filter2 = filter1 + filterSize*2
+        add             x4, x10, x7                 // filter3 = filter2 + filterSize*2
+        movi            v0.16b, #0                  // val sum part 1 (for dst[0])
+        movi            v1.16b, #0                  // val sum part 2 (for dst[1])
+        movi            v2.16b, #0                  // val sum part 3 (for dst[2])
+        movi            v3.16b, #0                  // val sum part 4 (for dst[3])
+        add             x17, x3, w8, uxtw           // srcp + filterPos[0]
+        add             x8,  x3, w0, uxtw           // srcp + filterPos[1]
+        add             x0, x3, w11, uxtw           // srcp + filterPos[2]
+        add             x11, x3, w9, uxtw           // srcp + filterPos[3]
+        mov             w15, w6                     // filterSize counter
+2:      ld1             {v4.8b}, [x17], #8          // srcp[filterPos[0] + {0..7}]
+        ld1             {v5.8h}, [x16], #16         // load 8x16-bit filter values, part 1
+        ld1             {v6.8b}, [x8], #8           // srcp[filterPos[1] + {0..7}]
+        ld1             {v7.8h}, [x12], #16         // load 8x16-bit at filter+filterSize
+        uxtl            v4.8h, v4.8b                // unpack part 1 to 16-bit
+        smlal           v0.4s, v4.4h, v5.4h         // v0 accumulates srcp[filterPos[0] + {0..3}] * filter[{0..3}]
+        smlal2          v0.4s, v4.8h, v5.8h         // v0 accumulates srcp[filterPos[0] + {4..7}] * filter[{4..7}]
+        ld1             {v4.8b}, [x0], #8           // srcp[filterPos[2] + {0..7}]
+        ld1             {v5.8h}, [x10], #16         // load 8x16-bit at filter+2*filterSize
+        uxtl            v6.8h, v6.8b                // unpack part 2 to 16-bit
+        smlal           v1.4s, v6.4h, v7.4h         // v1 accumulates srcp[filterPos[1] + {0..3}] * filter[{0..3}]
+        uxtl            v4.8h, v4.8b                // unpack part 3 to 16-bit
+        smlal           v2.4s, v4.4h, v5.4h         // v2 accumulates srcp[filterPos[2] + {0..3}] * filter[{0..3}]
+        smlal2          v2.4s, v4.8h, v5.8h         // v2 accumulates srcp[filterPos[2] + {4..7}] * filter[{4..7}]
+        ld1             {v4.8b}, [x11], #8          // srcp[filterPos[3] + {0..7}]
+        smlal2          v1.4s, v6.8h, v7.8h         // v1 accumulates srcp[filterPos[1] + {4..7}] * filter[{4..7}]
+        ld1             {v5.8h}, [x4], #16          // load 8x16-bit at filter+3*filterSize
+        subs            w15, w15, #8                // j -= 8: processed 8/filterSize
+        uxtl            v4.8h, v4.8b                // unpack part 4 to 16-bit
+        smlal           v3.4s, v4.4h, v5.4h         // v3 accumulates srcp[filterPos[3] + {0..3}] * filter[{0..3}]
+        smlal2          v3.4s, v4.8h, v5.8h         // v3 accumulates srcp[filterPos[3] + {4..7}] * filter[{4..7}]
+        b.gt            2b                          // inner loop if filterSize not consumed completely
+        addp            v0.4s, v0.4s, v1.4s         // part01 horizontal pair adding
+        addp            v2.4s, v2.4s, v3.4s         // part23 horizontal pair adding
+        addp            v0.4s, v0.4s, v2.4s         // part0123 horizontal pair adding
+        subs            w2, w2, #4                  // dstW -= 4
+        sqshrn          v0.4h, v0.4s, #7            // shift and clip the 2x16-bit final values
+        st1             {v0.4h}, [x1], #8           // write to destination part0123
+        b.gt            1b                          // loop until end of line
+        ret
+endfunc
+
+function ff_hscale8to15_X4_neon, export=1
+// x0  SwsInternal *c (not used)
+// x1  int16_t *dst
+// w2  int dstW
+// x3  const uint8_t *src
+// x4  const int16_t *filter
+// x5  const int32_t *filterPos
+// w6  int filterSize
+
+// This function for filter sizes that are 4 mod 8. In other words, anything that's 0 mod 4 but not
+// 0 mod 8. It also assumes that dstW is 0 mod 4.
+
+        stp             x19, x20, [sp, #-0x30]!
+        stp             d8, d9, [sp, #0x10]
+        stp             d10, d11, [sp, #0x20]
+
+        lsl             w7, w6, #1                  // w7 = filterSize * 2
+1:
+        ldp             w8, w9,  [x5]               // filterPos[idx + 0], [idx + 1]
+        ldp             w10, w11, [x5, #8]          // filterPos[idx + 2], [idx + 3]
+
+        movi            v8.16b, #0                  // initialize accumulator for idx + 0
+        movi            v9.16b, #0                  // initialize accumulator for idx + 1
+        movi            v10.16b, #0                 // initialize accumulator for idx + 2
+        movi            v11.16b, #0                 // initialize accumulator for idx + 3
+
+        mov             x12, x4                     // filter pointer for idx + 0
+        add             x19, x4, x7                 // filter pointer for idx + 1
+        add             x8, x3, w8, uxtw            // srcp + filterPos[idx + 0]
+        add             x9, x3, w9, uxtw            // srcp + filterPos[idx + 1]
+
+        add             x20, x19, x7                // filter pointer for idx + 2
+        add             x10, x3, w10, uxtw          // srcp + filterPos[idx + 2]
+        add             x11, x3, w11, uxtw          // srcp + filterPos[idx + 3]
+
+        mov             w0, w6                      // copy filterSize to a temp register, w0
+        add             x5, x5, #16                 // advance the filterPos pointer
+        add             x15, x20, x7                // filter pointer for idx + 3
+        mov             x16, xzr                    // temp register for offsetting filter pointers
+
+2:
+        // This section loops over 8-wide chunks of filter size
+        ldr             d4, [x8], #8                // load 8 bytes from srcp for idx + 0
+        ldr             q0, [x12, x16]              // load 8 values, 16 bytes from filter for idx + 0
+
+        ldr             d5, [x9], #8                // load 8 bytes from srcp for idx + 1
+        ldr             q1, [x19, x16]              // load 8 values, 16 bytes from filter for idx + 1
+
+        uxtl            v4.8h, v4.8b                // unsigned extend long for idx + 0
+        uxtl            v5.8h, v5.8b                // unsigned extend long for idx + 1
+
+        ldr             d6, [x10], #8               // load 8 bytes from srcp for idx + 2
+        ldr             q2, [x20, x16]              // load 8 values, 16 bytes from filter for idx + 2
+
+        smlal           v8.4s, v0.4h, v4.4h         // val += src[srcPos + j + 0..3] * filter[fs * i + j + 0..3], idx + 0
+        smlal           v9.4s, v1.4h, v5.4h         // val += src[srcPos + j + 0..3] * filter[fs * i + j + 0..3], idx + 1
+
+        ldr             d7, [x11], #8               // load 8 bytes from srcp for idx + 3
+        ldr             q3, [x15, x16]              // load 8 values, 16 bytes from filter for idx + 3
+
+        sub             w0, w0, #8                  // decrement the remaining filterSize counter
+        smlal2          v8.4s, v0.8h, v4.8h         // val += src[srcPos + j + 4..7] * filter[fs * i + j + 4..7], idx + 0
+        smlal2          v9.4s, v1.8h, v5.8h         // val += src[srcPos + j + 4..7] * filter[fs * i + j + 4..7], idx + 1
+        uxtl            v6.8h, v6.8b                // unsigned extend long for idx + 2
+        uxtl            v7.8h, v7.8b                // unsigned extend long for idx + 3
+        smlal           v10.4s, v2.4h, v6.4h        // val += src[srcPos + j + 0..3] * filter[fs * i + j + 0..3], idx + 2
+        smlal           v11.4s, v3.4h, v7.4h        // val += src[srcPos + j + 0..3] * filter[fs * i + j + 0..3], idx + 3
+
+        cmp             w0, #8                      // are there at least 8 more elements in filter to consume?
+        add             x16, x16, #16               // advance the offsetting register for filter values
+
+        smlal2          v10.4s, v2.8h, v6.8h        // val += src[srcPos + j + 4..7] * filter[fs * i + j + 4..7], idx + 2
+        smlal2          v11.4s, v3.8h, v7.8h        // val += src[srcPos + j + 4..7] * filter[fs * i + j + 4..7], idx + 3
+
+        b.ge            2b                          // branch back to inner loop
+
+        // complete the remaining 4 filter elements
+        sub             x17, x7, #8                 // calculate the offset of the filter pointer for the remaining 4 elements
+
+        ldr             s4, [x8]                    // load 4 bytes from srcp for idx + 0
+        ldr             d0, [x12, x17]              // load 4 values, 8 bytes from filter for idx + 0
+        ldr             s5, [x9]                    // load 4 bytes from srcp for idx + 1
+        ldr             d1, [x19, x17]              // load 4 values, 8 bytes from filter for idx + 1
+
+        uxtl            v4.8h, v4.8b                // unsigned extend long for idx + 0
+        uxtl            v5.8h, v5.8b                // unsigned extend long for idx + 1
+
+        ldr             s6, [x10]                   // load 4 bytes from srcp for idx + 2
+        ldr             d2, [x20, x17]              // load 4 values, 8 bytes from filter for idx + 2
+        smlal           v8.4s, v0.4h, v4.4h         // val += src[srcPos + j + 0..3] * filter[fs * i + j + 0..3], idx + 0
+        smlal           v9.4s, v1.4h, v5.4h         // val += src[srcPos + j + 0..3] * filter[fs * i + j + 0..3], idx + 1
+        ldr             s7, [x11]                   // load 4 bytes from srcp for idx + 3
+        ldr             d3, [x15, x17]              // load 4 values, 8 bytes from filter for idx + 3
+
+        uxtl            v6.8h, v6.8b                // unsigned extend long for idx + 2
+        uxtl            v7.8h, v7.8b                // unsigned extend long for idx + 3
+        addp            v8.4s, v8.4s, v9.4s         // horizontal pair adding for idx 0,1
+        smlal           v10.4s, v2.4h, v6.4h        // val += src[srcPos + j + 0..3] * filter[fs * i + j + 0..3], idx + 2
+        smlal           v11.4s, v3.4h, v7.4h        // val += src[srcPos + j + 0..3] * filter[fs * i + j + 0..3], idx + 3
+
+        addp            v10.4s, v10.4s, v11.4s      // horizontal pair adding for idx 2,3
+        addp            v8.4s, v8.4s, v10.4s        // final horizontal pair adding producing one vector with results for idx = 0..3
+
+        subs            w2, w2, #4                  // dstW -= 4
+        sqshrn          v0.4h, v8.4s, #7            // shift and clip the 2x16-bit final values
+        st1             {v0.4h}, [x1], #8           // write to destination idx 0..3
+        add             x4, x4, x7, lsl #2          // filter += (filterSize*2) * 4
+        b.gt            1b                          // loop until end of line
+
+        ldp             d8, d9, [sp, #0x10]
+        ldp             d10, d11, [sp, #0x20]
+        ldp             x19, x20, [sp], #0x30
+        ret
+endfunc
+
+function ff_hscale8to15_4_neon, export=1
+// x0  SwsInternal *c (not used)
+// x1  int16_t *dst
+// x2  int dstW
+// x3  const uint8_t *src
+// x4  const int16_t *filter
+// x5  const int32_t *filterPos
+// x6  int filterSize
+// x8-x17 registers for gathering src data
+
+// v0      madd accumulator 4S
+// v1-v4   filter values (16 bit) 8H
+// v5      madd accumulator 4S
+// v6-v9   src values (8 bit) 8B
+
+// This implementation has 4 sections:
+//  1. Prefetch src data
+//  2. Interleaved prefetching src data and madd
+//  3. Complete madd
+//  4. Complete remaining iterations when dstW % 8 != 0
+
+        stp             d8, d9, [sp, #-0x10]!
+        sub             sp, sp, #32                 // allocate 32 bytes on the stack
+        cmp             w2, #16                     // if dstW <16, skip to the last block used for wrapping up
+        b.lt            2f
+
+        // load 8 values from filterPos to be used as offsets into src
+        ldp             w8, w9,  [x5]               // filterPos[idx + 0], [idx + 1]
+        ldp             w10, w11, [x5, #8]          // filterPos[idx + 2], [idx + 3]
+        ldp             w12, w15, [x5, #16]         // filterPos[idx + 4], [idx + 5]
+        ldp             w16, w17, [x5, #24]         // filterPos[idx + 6], [idx + 7]
+        add             x5, x5, #32                 // advance filterPos
+
+        // gather random access data from src into contiguous memory
+        ldr             w8, [x3, w8, uxtw]          // src[filterPos[idx + 0]][0..3]
+        ldr             w9, [x3, w9, uxtw]          // src[filterPos[idx + 1]][0..3]
+        ldr             w10, [x3, w10, uxtw]        // src[filterPos[idx + 2]][0..3]
+        ldr             w11, [x3, w11, uxtw]        // src[filterPos[idx + 3]][0..3]
+        ldr             w12, [x3, w12, uxtw]        // src[filterPos[idx + 4]][0..3]
+        ldr             w15, [x3, w15, uxtw]        // src[filterPos[idx + 5]][0..3]
+        ldr             w16, [x3, w16, uxtw]        // src[filterPos[idx + 6]][0..3]
+        ldr             w17, [x3, w17, uxtw]        // src[filterPos[idx + 7]][0..3]
+        stp             w8, w9, [sp]                // *scratch_mem = { src[filterPos[idx + 0]][0..3], src[filterPos[idx + 1]][0..3] }
+        stp             w10, w11, [sp, #8]          // *scratch_mem = { src[filterPos[idx + 2]][0..3], src[filterPos[idx + 3]][0..3] }
+        stp             w12, w15, [sp, #16]         // *scratch_mem = { src[filterPos[idx + 4]][0..3], src[filterPos[idx + 5]][0..3] }
+        stp             w16, w17, [sp, #24]         // *scratch_mem = { src[filterPos[idx + 6]][0..3], src[filterPos[idx + 7]][0..3] }
+
+1:
+        ld4             {v6.8b, v7.8b, v8.8b, v9.8b}, [sp] // transpose 8 bytes each from src into 4 registers
+
+        // load 8 values from filterPos to be used as offsets into src
+        ldp             w8, w9,  [x5]               // filterPos[idx + 0][0..3], [idx + 1][0..3], next iteration
+        ldp             w10, w11, [x5, #8]          // filterPos[idx + 2][0..3], [idx + 3][0..3], next iteration
+        ldp             w12, w15, [x5, #16]         // filterPos[idx + 4][0..3], [idx + 5][0..3], next iteration
+        ldp             w16, w17, [x5, #24]         // filterPos[idx + 6][0..3], [idx + 7][0..3], next iteration
+
+        movi            v0.16b, #0                  // Clear madd accumulator for idx 0..3
+        movi            v5.16b, #0                  // Clear madd accumulator for idx 4..7
+
+        ld4             {v1.8h, v2.8h, v3.8h, v4.8h}, [x4], #64 // load filter idx + 0..7
+
+        add             x5, x5, #32                 // advance filterPos
+
+        // interleaved SIMD and prefetching intended to keep ld/st and vector pipelines busy
+        uxtl            v6.8h, v6.8b                // unsigned extend long, convert src data to 16-bit
+        uxtl            v7.8h, v7.8b                // unsigned extend long, convert src data to 16-bit
+        ldr             w8, [x3, w8, uxtw]          // src[filterPos[idx + 0]], next iteration
+        ldr             w9, [x3, w9, uxtw]          // src[filterPos[idx + 1]], next iteration
+        uxtl            v8.8h, v8.8b                // unsigned extend long, convert src data to 16-bit
+        uxtl            v9.8h, v9.8b                // unsigned extend long, convert src data to 16-bit
+        ldr             w10, [x3, w10, uxtw]        // src[filterPos[idx + 2]], next iteration
+        ldr             w11, [x3, w11, uxtw]        // src[filterPos[idx + 3]], next iteration
+
+        smlal           v0.4s, v1.4h, v6.4h         // multiply accumulate inner loop j = 0, idx = 0..3
+        smlal           v0.4s, v2.4h, v7.4h         // multiply accumulate inner loop j = 1, idx = 0..3
+        ldr             w12, [x3, w12, uxtw]        // src[filterPos[idx + 4]], next iteration
+        ldr             w15, [x3, w15, uxtw]        // src[filterPos[idx + 5]], next iteration
+        smlal           v0.4s, v3.4h, v8.4h         // multiply accumulate inner loop j = 2, idx = 0..3
+        smlal           v0.4s, v4.4h, v9.4h         // multiply accumulate inner loop j = 3, idx = 0..3
+        ldr             w16, [x3, w16, uxtw]        // src[filterPos[idx + 6]], next iteration
+        ldr             w17, [x3, w17, uxtw]        // src[filterPos[idx + 7]], next iteration
+
+        smlal2          v5.4s, v1.8h, v6.8h         // multiply accumulate inner loop j = 0, idx = 4..7
+        smlal2          v5.4s, v2.8h, v7.8h         // multiply accumulate inner loop j = 1, idx = 4..7
+        stp             w8, w9, [sp]                // *scratch_mem = { src[filterPos[idx + 0]][0..3], src[filterPos[idx + 1]][0..3] }
+        stp             w10, w11, [sp, #8]          // *scratch_mem = { src[filterPos[idx + 2]][0..3], src[filterPos[idx + 3]][0..3] }
+        smlal2          v5.4s, v3.8h, v8.8h         // multiply accumulate inner loop j = 2, idx = 4..7
+        smlal2          v5.4s, v4.8h, v9.8h         // multiply accumulate inner loop j = 3, idx = 4..7
+        stp             w12, w15, [sp, #16]         // *scratch_mem = { src[filterPos[idx + 4]][0..3], src[filterPos[idx + 5]][0..3] }
+        stp             w16, w17, [sp, #24]         // *scratch_mem = { src[filterPos[idx + 6]][0..3], src[filterPos[idx + 7]][0..3] }
+
+        sub             w2, w2, #8                  // dstW -= 8
+        sqshrn          v0.4h, v0.4s, #7            // shift and clip the 2x16-bit final values
+        sqshrn          v1.4h, v5.4s, #7            // shift and clip the 2x16-bit final values
+        st1             {v0.4h, v1.4h}, [x1], #16   // write to dst[idx + 0..7]
+        cmp             w2, #16                     // continue on main loop if there are at least 16 iterations left
+        b.ge            1b
+
+        // last full iteration
+        ld4             {v6.8b, v7.8b, v8.8b, v9.8b}, [sp]
+        ld4             {v1.8h, v2.8h, v3.8h, v4.8h}, [x4], #64 // load filter idx + 0..7
+
+        movi            v0.16b, #0                  // Clear madd accumulator for idx 0..3
+        movi            v5.16b, #0                  // Clear madd accumulator for idx 4..7
+
+        uxtl            v6.8h, v6.8b                // unsigned extend long, convert src data to 16-bit
+        uxtl            v7.8h, v7.8b                // unsigned extend long, convert src data to 16-bit
+        uxtl            v8.8h, v8.8b                // unsigned extend long, convert src data to 16-bit
+        uxtl            v9.8h, v9.8b                // unsigned extend long, convert src data to 16-bit
+
+        smlal           v0.4s, v1.4h, v6.4h         // multiply accumulate inner loop j = 0, idx = 0..3
+        smlal           v0.4s, v2.4h, v7.4h         // multiply accumulate inner loop j = 1, idx = 0..3
+        smlal           v0.4s, v3.4h, v8.4h         // multiply accumulate inner loop j = 2, idx = 0..3
+        smlal           v0.4s, v4.4h, v9.4h         // multiply accumulate inner loop j = 3, idx = 0..3
+
+        smlal2          v5.4s, v1.8h, v6.8h         // multiply accumulate inner loop j = 0, idx = 4..7
+        smlal2          v5.4s, v2.8h, v7.8h         // multiply accumulate inner loop j = 1, idx = 4..7
+        smlal2          v5.4s, v3.8h, v8.8h         // multiply accumulate inner loop j = 2, idx = 4..7
+        smlal2          v5.4s, v4.8h, v9.8h         // multiply accumulate inner loop j = 3, idx = 4..7
+
+        subs            w2, w2, #8                  // dstW -= 8
+        sqshrn          v0.4h, v0.4s, #7            // shift and clip the 2x16-bit final values
+        sqshrn          v1.4h, v5.4s, #7            // shift and clip the 2x16-bit final values
+        st1             {v0.4h, v1.4h}, [x1], #16   // write to dst[idx + 0..7]
+
+        cbnz            w2, 2f                      // if >0 iterations remain, jump to the wrap up section
+
+        add             sp, sp, #32                 // clean up stack
+        ldp             d8, d9, [sp], #0x10
+        ret
+
+        // finish up when dstW % 8 != 0 or dstW < 16
+2:
+        // load src
+        ldr             w8, [x5], #4                // filterPos[i]
+        add             x9, x3, w8, uxtw            // calculate the address for src load
+        ld1             {v5.s}[0], [x9]             // src[filterPos[i] + 0..3]
+        // load filter
+        ld1             {v6.4h}, [x4], #8           // filter[filterSize * i + 0..3]
+
+        uxtl            v5.8h, v5.8b                // unsigned extend long, convert src data to 16-bit
+        smull           v0.4s, v5.4h, v6.4h         // 4 iterations of src[...] * filter[...]
+        addv            s0, v0.4s                   // add up products of src and filter values
+        sqshrn          h0, s0, #7                  // shift and clip the 2x16-bit final value
+        st1             {v0.h}[0], [x1], #2         // dst[i] = ...
+        sub             w2, w2, #1                  // dstW--
+        cbnz            w2, 2b
+
+        add             sp, sp, #32                 // clean up stack
+        ldp             d8, d9, [sp], #0x10
+        ret
+endfunc
+
+function ff_hscale8to19_4_neon, export=1
+        // x0               SwsInternal *c (unused)
+        // x1               int32_t *dst
+        // w2               int dstW
+        // x3               const uint8_t *src // treat it as uint16_t *src
+        // x4               const uint16_t *filter
+        // x5               const int32_t *filterPos
+        // w6               int filterSize
+
+        movi            v7.4s, #1
+        movi            v0.4s, #1
+        shl             v7.4s, v7.4s, #19
+        sub             v7.4s, v7.4s, v0.4s      // max allowed value
+
+        cmp             w2, #16
+        b.lt            2f // move to last block
+
+        ldp             w8, w9, [x5]                // filterPos[0], filterPos[1]
+        ldp             w10, w11, [x5, #8]          // filterPos[2], filterPos[3]
+        ldp             w12, w15, [x5, #16]         // filterPos[4], filterPos[5]
+        ldp             w16, w17, [x5, #24]         // filterPos[6], filterPos[7]
+        add             x5, x5, #32
+
+        // load data from
+        ldr             w8, [x3, w8, uxtw]
+        ldr             w9, [x3, w9, uxtw]
+        ldr             w10, [x3, w10, uxtw]
+        ldr             w11, [x3, w11, uxtw]
+        ldr             w12, [x3, w12, uxtw]
+        ldr             w15, [x3, w15, uxtw]
+        ldr             w16, [x3, w16, uxtw]
+        ldr             w17, [x3, w17, uxtw]
+
+        stp             d8, d9, [sp, #-0x20]!
+        stp             d10, d11, [sp, #0x10]
+        sub             sp, sp, #32
+
+        stp             w8, w9, [sp]
+        stp             w10, w11, [sp, #8]
+        stp             w12, w15, [sp, #16]
+        stp             w16, w17, [sp, #24]
+
+1:
+        ld4             {v0.8b, v1.8b, v2.8b, v3.8b}, [sp]
+        ld4             {v8.8h, v9.8h, v10.8h, v11.8h}, [x4], #64 // filter[0..7]
+        // load filterPositions into registers for next iteration
+
+        ldp             w8, w9, [x5]                // filterPos[0], filterPos[1]
+        ldp             w10, w11, [x5, #8]          // filterPos[2], filterPos[3]
+        ldp             w12, w15, [x5, #16]         // filterPos[4], filterPos[5]
+        ldp             w16, w17, [x5, #24]         // filterPos[6], filterPos[7]
+        add             x5, x5, #32
+        uxtl            v0.8h, v0.8b
+        ldr             w8, [x3, w8, uxtw]
+        smull           v5.4s, v0.4h, v8.4h        // multiply first column of src
+        ldr             w9, [x3, w9, uxtw]
+        smull2          v6.4s, v0.8h, v8.8h
+        stp             w8, w9, [sp]
+
+        uxtl            v1.8h, v1.8b
+        ldr             w10, [x3, w10, uxtw]
+        smlal           v5.4s, v1.4h, v9.4h        // multiply second column of src
+        ldr             w11, [x3, w11, uxtw]
+        smlal2          v6.4s, v1.8h, v9.8h
+        stp             w10, w11, [sp, #8]
+
+        uxtl            v2.8h, v2.8b
+        ldr             w12, [x3, w12, uxtw]
+        smlal           v5.4s, v2.4h, v10.4h        // multiply third column of src
+        ldr             w15, [x3, w15, uxtw]
+        smlal2          v6.4s, v2.8h, v10.8h
+        stp             w12, w15, [sp, #16]
+
+        uxtl            v3.8h, v3.8b
+        ldr             w16, [x3, w16, uxtw]
+        smlal           v5.4s, v3.4h, v11.4h        // multiply fourth column of src
+        ldr             w17, [x3, w17, uxtw]
+        smlal2          v6.4s, v3.8h, v11.8h
+        stp             w16, w17, [sp, #24]
+
+        sub             w2, w2, #8
+        sshr            v5.4s, v5.4s, #3
+        sshr            v6.4s, v6.4s, #3
+        smin            v5.4s, v5.4s, v7.4s
+        smin            v6.4s, v6.4s, v7.4s
+
+        st1             {v5.4s, v6.4s}, [x1], #32
+        cmp             w2, #16
+        b.ge            1b
+
+        // here we make last iteration, without updating the registers
+        ld4             {v0.8b, v1.8b, v2.8b, v3.8b}, [sp]
+        ld4             {v8.8h, v9.8h, v10.8h, v11.8h}, [x4], #64 // filter[0..7]
+
+        uxtl            v0.8h, v0.8b
+        uxtl            v1.8h, v1.8b
+        smull           v5.4s, v0.4h, v8.4h
+        smull2          v6.4s, v0.8h, v8.8h
+        uxtl            v2.8h, v2.8b
+        smlal           v5.4s, v1.4h, v9.4h
+        smlal2          v6.4s, v1.8h, v9.8h
+        uxtl            v3.8h, v3.8b
+        smlal           v5.4s, v2.4h, v10.4h
+        smlal2          v6.4s, v2.8h, v10.8h
+        smlal           v5.4s, v3.4h, v11.4h
+        smlal2          v6.4s, v3.8h, v11.8h
+
+        sshr            v5.4s, v5.4s, #3
+        sshr            v6.4s, v6.4s, #3
+
+        smin            v5.4s, v5.4s, v7.4s
+        smin            v6.4s, v6.4s, v7.4s
+
+        sub             w2, w2, #8
+        st1             {v5.4s, v6.4s}, [x1], #32
+        add             sp, sp, #32 // restore stack
+        ldp             d10, d11, [sp, #0x10]
+        ldp             d8, d9, [sp], #0x20
+        cbnz            w2, 2f
+
+        ret
+
+2:
+        ldr             w8, [x5], #4 // load filterPos
+        add             x9, x3, w8, uxtw // src + filterPos
+        ld1             {v0.s}[0], [x9] // load 4 * uint8_t* into one single
+        ld1             {v1.4h}, [x4], #8
+        uxtl            v0.8h, v0.8b
+        smull           v5.4s, v0.4h, v1.4h
+        saddlv          d0, v5.4s
+        sqshrn          s0, d0, #3
+        smin            v0.4s, v0.4s, v7.4s
+        st1             {v0.s}[0], [x1], #4
+        sub             w2, w2, #1
+        cbnz            w2, 2b // if iterations remain jump to beginning
+
+        ret
+endfunc
+
+function ff_hscale8to19_X8_neon, export=1
+        stp             d8, d9, [sp, #-0x10]!
+        movi            v8.4s, #1
+        movi            v9.4s, #1
+        shl             v8.4s, v8.4s, #19
+        sub             v8.4s, v8.4s, v9.4s
+
+        sbfiz           x7, x6, #1, #32             // filterSize*2 (*2 because int16)
+1:
+        mov             x16, x4                     // filter0 = filter
+        ldr             w8, [x5], #4                // filterPos[idx]
+        add             x12, x16, x7                // filter1 = filter0 + filterSize*2
+        ldr             w0, [x5], #4                // filterPos[idx + 1]
+        add             x10, x12, x7                // filter2 = filter1 + filterSize*2
+        ldr             w11, [x5], #4               // filterPos[idx + 2]
+        add             x4, x10, x7                 // filter3 = filter2 + filterSize*2
+        ldr             w9, [x5], #4                // filterPos[idx + 3]
+        movi            v0.16b, #0                  // val sum part 1 (for dst[0])
+        movi            v1.16b, #0                  // val sum part 2 (for dst[1])
+        movi            v2.16b, #0                  // val sum part 3 (for dst[2])
+        movi            v3.16b, #0                  // val sum part 4 (for dst[3])
+        add             x17, x3, w8, uxtw           // srcp + filterPos[0]
+        add             x8,  x3, w0, uxtw           // srcp + filterPos[1]
+        add             x0, x3, w11, uxtw           // srcp + filterPos[2]
+        add             x11, x3, w9, uxtw           // srcp + filterPos[3]
+        mov             w15, w6                     // filterSize counter
+2:      ld1             {v4.8b}, [x17], #8          // srcp[filterPos[0] + {0..7}]
+        ld1             {v5.8h}, [x16], #16         // load 8x16-bit filter values, part 1
+        uxtl            v4.8h, v4.8b                // unpack part 1 to 16-bit
+        smlal           v0.4s, v4.4h, v5.4h         // v0 accumulates srcp[filterPos[0] + {0..3}] * filter[{0..3}]
+        ld1             {v6.8b}, [x8], #8           // srcp[filterPos[1] + {0..7}]
+        smlal2          v0.4s, v4.8h, v5.8h         // v0 accumulates srcp[filterPos[0] + {4..7}] * filter[{4..7}]
+        ld1             {v7.8h}, [x12], #16         // load 8x16-bit at filter+filterSize
+        ld1             {v4.8b}, [x0], #8           // srcp[filterPos[2] + {0..7}]
+        uxtl            v6.8h, v6.8b                // unpack part 2 to 16-bit
+        ld1             {v5.8h}, [x10], #16         // load 8x16-bit at filter+2*filterSize
+        smlal           v1.4s, v6.4h, v7.4h         // v1 accumulates srcp[filterPos[1] + {0..3}] * filter[{0..3}]
+        uxtl            v4.8h, v4.8b                // unpack part 3 to 16-bit
+        smlal2          v1.4s, v6.8h, v7.8h         // v1 accumulates srcp[filterPos[1] + {4..7}] * filter[{4..7}]
+        ld1             {v6.8b}, [x11], #8          // srcp[filterPos[3] + {0..7}]
+        smlal           v2.4s, v4.4h, v5.4h         // v2 accumulates srcp[filterPos[2] + {0..3}] * filter[{0..3}]
+        ld1             {v7.8h}, [x4], #16          // load 8x16-bit at filter+3*filterSize
+        smlal2          v2.4s, v4.8h, v5.8h         // v2 accumulates srcp[filterPos[2] + {4..7}] * filter[{4..7}]
+        uxtl            v6.8h, v6.8b                // unpack part 4 to 16-bit
+        smlal           v3.4s, v6.4h, v7.4h         // v3 accumulates srcp[filterPos[3] + {0..3}] * filter[{0..3}]
+        subs            w15, w15, #8                // j -= 8: processed 8/filterSize
+        smlal2          v3.4s, v6.8h, v7.8h         // v3 accumulates srcp[filterPos[3] + {4..7}] * filter[{4..7}]
+        b.gt            2b                          // inner loop if filterSize not consumed completely
+        addp            v0.4s, v0.4s, v1.4s         // part01 horizontal pair adding
+        addp            v2.4s, v2.4s, v3.4s         // part23 horizontal pair adding
+        addp            v0.4s, v0.4s, v2.4s         // part0123 horizontal pair adding
+        subs            w2, w2, #4                  // dstW -= 4
+        sshr            v0.4s, v0.4s, #3            // shift and clip the 2x16-bit final values
+        smin            v0.4s, v0.4s, v8.4s
+        st1             {v0.4s}, [x1], #16           // write to destination part0123
+        b.gt            1b                          // loop until end of line
+        ldp             d8, d9, [sp], #0x10
+        ret
+endfunc
+
+function ff_hscale8to19_X4_neon, export=1
+        // x0  SwsInternal *c (not used)
+        // x1  int16_t *dst
+        // w2  int dstW
+        // x3  const uint8_t *src
+        // x4  const int16_t *filter
+        // x5  const int32_t *filterPos
+        // w6  int filterSize
+
+        stp             x19, x20, [sp, #-0x20]!
+        stp             d8, d9, [sp, #0x10]
+        movi            v8.4s, #1
+        movi            v9.4s, #1
+        shl             v8.4s, v8.4s, #19
+        sub             v8.4s, v8.4s, v9.4s
+
+        lsl             w7, w6, #1
+1:
+        ldp             w8, w9, [x5]
+        ldp             w10, w11, [x5, #8]
+
+        movi            v0.16b, #0                 // initialize accumulator for idx + 0
+        movi            v1.16b, #0                 // initialize accumulator for idx + 1
+        movi            v2.16b, #0                 // initialize accumulator for idx + 2
+        movi            v3.16b, #0                 // initialize accumulator for idx + 3
+
+        mov             x12, x4                     // filter + 0
+        add             x19, x4, x7                 // filter + 1
+        add             x8, x3, w8, uxtw            // srcp + filterPos 0
+        add             x20, x19, x7                // filter + 2
+        add             x9, x3, w9, uxtw            // srcp + filterPos 1
+        add             x15, x20, x7                // filter + 3
+        add             x10, x3, w10, uxtw          // srcp + filterPos 2
+        mov             w0, w6                      // save the filterSize to temporary variable
+        add             x11, x3, w11, uxtw          // srcp + filterPos 3
+        add             x5, x5, #16                 // advance filter position
+        mov             x16, xzr                    // clear the register x16 used for offsetting the filter values
+
+2:
+        ldr             d4, [x8], #8                // load src values for idx 0
+        ldr             q6, [x12, x16]              // load filter values for idx 0
+        uxtl            v4.8h, v4.8b                // extend type to match the filter' size
+        ldr             d5, [x9], #8                // load src values for idx 1
+        smlal           v0.4s, v4.4h, v6.4h         // multiplication of lower half for idx 0
+        uxtl            v5.8h, v5.8b                // extend type to match the filter' size
+        ldr             q9, [x19, x16]              // load filter values for idx 1
+        smlal2          v0.4s, v4.8h, v6.8h         // multiplication of upper half for idx 0
+        ldr             d6, [x10], #8               // load src values for idx 2
+        ldr             q4, [x20, x16]              // load filter values for idx 2
+        smlal           v1.4s, v5.4h, v9.4h         // multiplication of lower half for idx 1
+        ldr             d7, [x11], #8               // load src values for idx 3
+        smlal2          v1.4s, v5.8h, v9.8h         // multiplication of upper half for idx 1
+        uxtl            v6.8h, v6.8b                // extend tpye to matchi the filter's size
+        ldr             q5, [x15, x16]              // load filter values for idx 3
+        smlal           v2.4s, v6.4h, v4.4h         // multiplication of lower half for idx 2
+        uxtl            v7.8h, v7.8b
+        smlal2          v2.4s, v6.8h, v4.8h         // multiplication of upper half for idx 2
+        sub             w0, w0, #8
+        smlal           v3.4s, v7.4h, v5.4h         // multiplication of lower half for idx 3
+        cmp             w0, #8
+        smlal2          v3.4s, v7.8h, v5.8h         // multiplication of upper half for idx 3
+        add             x16, x16, #16                // advance filter values indexing
+
+        b.ge            2b
+
+
+        // 4 iterations left
+
+        sub             x17, x7, #8                 // step back to wrap up the filter pos for last 4 elements
+
+        ldr             s4, [x8]                    // load src values for idx 0
+        ldr             d6, [x12, x17]              // load filter values for idx 0
+        uxtl            v4.8h, v4.8b                // extend type to match the filter' size
+        ldr             s5, [x9]                    // load src values for idx 1
+        smlal           v0.4s, v4.4h, v6.4h
+        ldr             d9, [x19, x17]              // load filter values for idx 1
+        uxtl            v5.8h, v5.8b                // extend type to match the filter' size
+        ldr             s6, [x10]                   // load src values for idx 2
+        smlal           v1.4s, v5.4h, v9.4h
+        uxtl            v6.8h, v6.8b                // extend type to match the filter's size
+        ldr             d4, [x20, x17]              // load filter values for idx 2
+        ldr             s7, [x11]                   // load src values for idx 3
+        addp            v0.4s, v0.4s, v1.4s
+        uxtl            v7.8h, v7.8b
+        ldr             d5, [x15, x17]              // load filter values for idx 3
+        smlal           v2.4s, v6.4h, v4.4h
+        smlal           v3.4s, v7.4h, v5.4h
+        subs            w2, w2, #4
+        addp            v2.4s, v2.4s, v3.4s
+        addp            v0.4s, v0.4s, v2.4s
+        sshr            v0.4s, v0.4s, #3
+        smin            v0.4s, v0.4s, v8.4s
+
+        st1             {v0.4s}, [x1], #16
+        add             x4, x4, x7, lsl #2
+        b.gt            1b
+        ldp             d8, d9, [sp, #0x10]
+        ldp             x19, x20, [sp], #0x20
+        ret
+endfunc
+
+
+.macro hscale_iter src, src2, filter, dst
+        uxtl            \src\().4s, \src\().4h
+        sxtl            v11.4s, \filter\().4h
+        mul             \dst\().4s, \src\().4s, v11.4s
+        uxtl            \src2\().4s, \src2\().4h
+        sxtl2           \filter\().4s, \filter\().8h
+        mul             v11.4s, \src2\().4s, \filter\().4s
+        addp            \dst\().4s, \dst\().4s, v11.4s
+.endm
+
+function ff_hscale16to15_4_neon_asm, export=1
+        // w0               int shift
+        // x1               int32_t *dst
+        // w2               int dstW
+        // x3               const uint8_t *src // treat it as uint16_t *src
+        // x4               const uint16_t *filter
+        // x5               const int32_t *filterPos
+        // w6               int filterSize
+
+        stp             d8, d9, [sp, #-0x40]!
+        stp             d10, d11, [sp, #0x10]
+        stp             d12, d13, [sp, #0x20]
+        stp             d14, d15, [sp, #0x30]
+
+        movi            v10.4s, #1
+        movi            v9.4s, #1
+        shl             v10.4s, v10.4s, #15
+        sub             v10.4s, v10.4s, v9.4s      // max allowed value
+        dup             v9.4s, w0                  // read shift
+        neg             v9.4s, v9.4s              // negate it, so it can be used in sshl (effectively shift right)
+
+        cmp             w2, #16
+        b.lt            2f // move to last block
+
+        ldp             w8, w9, [x5]                // filterPos[0], filterPos[1]
+        ldp             w10, w11, [x5, #8]          // filterPos[2], filterPos[3]
+        ldp             w12, w15, [x5, #16]         // filterPos[4], filterPos[5]
+        ldp             w16, w17, [x5, #24]         // filterPos[6], filterPos[7]
+        add             x5, x5, #32
+
+        // shift all filterPos left by one, as uint16_t will be read
+        ld1             {v12.8h, v13.8h, v14.8h, v15.8h}, [x4], #64 // filter[0..7]
+        lsl             x8, x8, #1
+        lsl             x9, x9, #1
+        lsl             x10, x10, #1
+        lsl             x11, x11, #1
+        lsl             x12, x12, #1
+        lsl             x15, x15, #1
+        lsl             x16, x16, #1
+        lsl             x17, x17, #1
+
+        // load src with given offset
+        ldr             d0, [x3, w8,  uxtw]
+        ldr             d1, [x3, w9,  uxtw]
+        ldr             d2, [x3, w10, uxtw]
+        ldr             d3, [x3, w11, uxtw]
+        ldr             d4, [x3, w12, uxtw]
+        ldr             d5, [x3, w15, uxtw]
+        ldr             d6, [x3, w16, uxtw]
+        ldr             d7, [x3, w17, uxtw]
+
+1:
+
+        // load filterPositions into registers for next iteration
+
+        hscale_iter     v0, v1, v12, v0
+        ldp             w8, w9, [x5]                // filterPos[0], filterPos[1]
+        hscale_iter     v2, v3, v13, v2
+        ldp             w10, w11, [x5, #8]          // filterPos[2], filterPos[3]
+        hscale_iter     v4, v5, v14, v4
+        ldp             w12, w15, [x5, #16]         // filterPos[4], filterPos[5]
+        hscale_iter     v6, v7, v15, v6
+        ldp             w16, w17, [x5, #24]         // filterPos[6], filterPos[7]
+        subs            w2, w2, #8
+        add             x5, x5, #32
+
+        ldp             q12, q13, [x4], #32 // filter[0..7]
+        lsl             x8, x8, #1
+        lsl             x9, x9, #1
+        lsl             x10, x10, #1
+        lsl             x11, x11, #1
+        ldp             q14, q15, [x4], #32 // filter[0..7]
+        lsl             x12, x12, #1
+        lsl             x15, x15, #1
+        lsl             x16, x16, #1
+        lsl             x17, x17, #1
+
+        addp            v8.4s, v0.4s, v2.4s
+        ldr             d0, [x3, w8,  uxtw]
+        ldr             d1, [x3, w9,  uxtw]
+        ldr             d2, [x3, w10, uxtw]
+        ldr             d3, [x3, w11, uxtw]
+        addp            v4.4s, v4.4s, v6.4s
+        ldr             d5, [x3, w15, uxtw]
+        cmp             w2, #16
+
+        sshl            v8.4s, v8.4s, v9.4s
+        ldr             d6, [x3, w16, uxtw]
+        sshl            v4.4s, v4.4s, v9.4s
+        ldr             d7, [x3, w17, uxtw]
+        smin            v8.4s, v8.4s, v10.4s
+        smin            v4.4s, v4.4s, v10.4s
+        xtn             v8.4h, v8.4s
+        xtn2            v8.8h, v4.4s
+        ldr             d4, [x3, w12, uxtw]
+
+        st1             {v8.8h}, [x1], #16
+
+        b.ge            1b
+
+        // here we make last iteration, without updating the registers
+
+        hscale_iter     v0, v1, v12, v0
+        hscale_iter     v2, v3, v13, v2
+        hscale_iter     v4, v5, v14, v4
+        hscale_iter     v6, v7, v15, v6
+        subs            w2, w2, #8
+
+        addp            v8.4s, v0.4s, v2.4s
+        addp            v1.4s, v4.4s, v6.4s
+
+        sshl            v8.4s, v8.4s, v9.4s
+        sshl            v1.4s, v1.4s, v9.4s
+        smin            v8.4s, v8.4s, v10.4s
+        smin            v1.4s, v1.4s, v10.4s
+        xtn             v8.4h, v8.4s
+        xtn2            v8.8h, v1.4s
+
+        st1             {v8.8h}, [x1], #16
+        cbz             w2, 3f
+2:
+        ldr             w8, [x5], #4                // load filterPos
+        add             x9, x3, w8, uxtw #1         // src + filterPos
+        ld1             {v0.4h}, [x9]               // load 4 * uint16_t
+        ld1             {v15.4h}, [x4], #8
+        sub             w2, w2, #1
+
+        uxtl            v0.4s, v0.4h
+        sxtl            v15.4s, v15.4h
+        mul             v5.4s, v0.4s, v15.4s
+        addv            s0, v5.4s
+        sshl            v0.4s, v0.4s, v9.4s
+        smin            v0.4s, v0.4s, v10.4s
+        st1             {v0.h}[0], [x1], #2
+        cbnz            w2, 2b                      // if iterations remain jump to beginning
+3:
+        ldp             d10, d11, [sp, #0x10]
+        ldp             d12, d13, [sp, #0x20]
+        ldp             d14, d15, [sp, #0x30]
+        ldp             d8, d9, [sp], #0x40
+        ret
+endfunc
+
+function ff_hscale16to15_X8_neon_asm, export=1
+        // w0               int shift
+        // x1               int32_t *dst
+        // w2               int dstW
+        // x3               const uint8_t *src // treat it as uint16_t *src
+        // x4               const uint16_t *filter
+        // x5               const int32_t *filterPos
+        // w6               int filterSize
+
+        stp             d8, d9, [sp, #-0x20]!
+        stp             d10, d11, [sp, #0x10]
+        movi            v8.4s, #1
+        movi            v9.4s, #1
+        shl             v8.4s, v8.4s, #15
+        sub             v8.4s, v8.4s, v9.4s
+        dup             v9.4s, w0
+        neg             v9.4s, v9.4s
+
+        sbfiz           x7, x6, #1, #32             // filterSize*2 (*2 because int16)
+1:      ldr             w8, [x5], #4                // filterPos[idx]
+        lsl             w8, w8, #1
+        ldr             w10, [x5], #4               // filterPos[idx + 1]
+        lsl             w10, w10, #1
+        ldr             w11, [x5], #4               // filterPos[idx + 2]
+        lsl             w11, w11, #1
+        ldr             w9, [x5], #4                // filterPos[idx + 3]
+        lsl             w9, w9, #1
+        movi            v0.16b, #0                  // val sum part 1 (for dst[0])
+        movi            v1.16b, #0                  // val sum part 2 (for dst[1])
+        movi            v2.16b, #0                  // val sum part 3 (for dst[2])
+        movi            v3.16b, #0                  // val sum part 4 (for dst[3])
+        add             x17, x3, w8, uxtw           // srcp + filterPos[0]
+        add             x8,  x3, w10, uxtw          // srcp + filterPos[1]
+        add             x10, x3, w11, uxtw          // srcp + filterPos[2]
+        add             x11, x3, w9, uxtw           // srcp + filterPos[3]
+        mov             x16, x4                     // filter0 = filter
+        add             x12, x16, x7                // filter1 = filter0 + filterSize*2
+        add             x9, x12, x7                 // filter2 = filter1 + filterSize*2
+        add             x4, x9, x7                  // filter3 = filter2 + filterSize*2
+        mov             w15, w6                     // filterSize counter
+2:      ld1             {v4.8h}, [x17], #16         // srcp[filterPos[0] + {0..7}]
+        ld1             {v5.8h}, [x16], #16         // load 8x16-bit filter values, part 1
+        ld1             {v6.8h}, [x8], #16          // srcp[filterPos[1] + {0..7}]
+        ld1             {v7.8h}, [x12], #16         // load 8x16-bit at filter+filterSize
+        uxtl            v10.4s, v4.4h               // extend srcp lower half to 32 bits to preserve sign
+        sxtl            v11.4s, v5.4h               // extend filter lower half to 32 bits to match srcp size
+        uxtl2           v4.4s, v4.8h                // extend srcp upper half to 32 bits
+        mla             v0.4s, v10.4s, v11.4s       // multiply accumulate lower half of v4 * v5
+        sxtl2           v5.4s, v5.8h                // extend filter upper half to 32 bits
+        uxtl            v10.4s, v6.4h               // extend srcp lower half to 32 bits
+        mla             v0.4s, v4.4s, v5.4s         // multiply accumulate upper half of v4 * v5
+        sxtl            v11.4s, v7.4h               // extend filter lower half
+        uxtl2           v6.4s, v6.8h                // extend srcp upper half
+        sxtl2           v7.4s, v7.8h                // extend filter upper half
+        ld1             {v4.8h}, [x10], #16         // srcp[filterPos[2] + {0..7}]
+        mla             v1.4s, v10.4s, v11.4s       // v1 accumulates srcp[filterPos[1] + {0..3}] * filter[{0..3}]
+        ld1             {v5.8h}, [x9], #16          // load 8x16-bit at filter+2*filterSize
+        uxtl            v10.4s, v4.4h               // extend srcp lower half
+        sxtl            v11.4s, v5.4h               // extend filter lower half
+        uxtl2           v4.4s, v4.8h                // extend srcp upper half
+        sxtl2           v5.4s, v5.8h                // extend filter upper half
+        mla             v2.4s, v10.4s, v11.4s       // v2 accumulates srcp[filterPos[2] + {0..3}] * filter[{0..3}]
+        mla             v2.4s, v4.4s, v5.4s         // v2 accumulates srcp[filterPos[2] + {4..7}] * filter[{4..7}]
+        ld1             {v4.8h}, [x11], #16         // srcp[filterPos[3] + {0..7}]
+        mla             v1.4s, v6.4s, v7.4s         // v1 accumulates srcp[filterPos[1] + {4..7}] * filter[{4..7}]
+        ld1             {v5.8h}, [x4], #16          // load 8x16-bit at filter+3*filterSize
+        subs            w15, w15, #8                // j -= 8: processed 8/filterSize
+        uxtl            v10.4s, v4.4h               // extend srcp lower half
+        sxtl            v11.4s, v5.4h               // extend filter lower half
+        uxtl2           v4.4s, v4.8h                // extend srcp upper half
+        sxtl2           v5.4s, v5.8h                // extend filter upper half
+        mla             v3.4s, v10.4s, v11.4s       // v3 accumulates srcp[filterPos[3] + {0..3}] * filter[{0..3}]
+        mla             v3.4s, v4.4s, v5.4s         // v3 accumulates srcp[filterPos[3] + {4..7}] * filter[{4..7}]
+        b.gt            2b                          // inner loop if filterSize not consumed completely
+        addp            v0.4s, v0.4s, v1.4s         // part01 horizontal pair adding
+        addp            v2.4s, v2.4s, v3.4s         // part23 horizontal pair adding
+        addp            v0.4s, v0.4s, v2.4s         // part0123 horizontal pair adding
+        subs            w2, w2, #4                  // dstW -= 4
+        sshl            v0.4s, v0.4s, v9.4s         // shift right (effectively right, as shift is negative); overflow expected
+        smin            v0.4s, v0.4s, v8.4s         // apply min (do not use sqshl)
+        xtn             v0.4h, v0.4s                // narrow down to 16 bits
+
+        st1             {v0.4h}, [x1], #8           // write to destination part0123
+        b.gt            1b                          // loop until end of line
+        ldp             d10, d11, [sp, #0x10]
+        ldp             d8, d9, [sp], #0x20
+        ret
+endfunc
+
+function ff_hscale16to15_X4_neon_asm, export=1
+        // w0  int shift
+        // x1  int16_t *dst
+        // w2  int dstW
+        // x3  const uint8_t *src
+        // x4  const int16_t *filter
+        // x5  const int32_t *filterPos
+        // w6  int filterSize
+
+        stp             x19, x20, [sp, #-0x40]!
+        stp             d8, d9, [sp, #0x10]
+        stp             d10, d11, [sp, #0x20]
+        stp             d12, d13, [sp, #0x30]
+
+        movi            v2.4s, #1
+        movi            v1.4s, #1
+        shl             v2.4s, v2.4s, #15
+        sub             v9.4s, v2.4s, v1.4s      // max allowed value
+        dup             v1.4s, w0                  // read shift
+        neg             v8.4s, v1.4s              // negate it, so it can be used in sshl (effectively shift right)
+
+        lsl             w7, w6, #1
+1:
+        ldp             w8, w9, [x5]
+        ldp             w10, w11, [x5, #8]
+
+        movi            v0.16b, #0                 // initialize accumulator for idx + 0
+        movi            v1.16b, #0                 // initialize accumulator for idx + 1
+        movi            v2.16b, #0                 // initialize accumulator for idx + 2
+        movi            v3.16b, #0                 // initialize accumulator for idx + 3
+
+        mov             x12, x4                     // filter + 0
+        add             x19, x4, x7                 // filter + 1
+        add             x8, x3, x8, lsl #1          // srcp + filterPos 0
+        add             x20, x19, x7                // filter + 2
+        add             x9, x3, x9, lsl #1          // srcp + filterPos 1
+        add             x15, x20, x7                // filter + 3
+        add             x10, x3, x10, lsl #1        // srcp + filterPos 2
+        mov             w0, w6                      // save the filterSize to temporary variable
+        add             x11, x3, x11, lsl #1        // srcp + filterPos 3
+        add             x5, x5, #16                 // advance filter position
+        mov             x16, xzr                    // clear the register x16 used for offsetting the filter values
+
+2:
+        ldr             q4, [x8], #16               // load src values for idx 0
+        ldr             q5, [x9], #16               // load src values for idx 1
+        uxtl            v11.4s, v4.4h
+        uxtl2           v4.4s, v4.8h
+        ldr             q7, [x12, x16]             // load filter values for idx 0
+        ldr             q6, [x10], #16              // load src values for idx 2
+        sxtl            v10.4s, v7.4h
+        sxtl2           v7.4s, v7.8h
+        mla             v0.4s, v11.4s, v10.4s      // multiplication of lower half for idx 0
+        uxtl            v10.4s, v5.4h
+        uxtl2           v5.4s, v5.8h
+        mla             v0.4s, v4.4s, v7.4s       // multiplication of upper half for idx 0
+        ldr             q12, [x19, x16]             // load filter values for idx 1
+        ldr             q7, [x11], #16              // load src values for idx 3
+        uxtl            v11.4s, v6.4h
+        sxtl            v4.4s, v12.4h
+        sxtl2           v12.4s, v12.8h
+        mla             v1.4s, v10.4s, v4.4s       // multiplication of lower half for idx 1
+        ldr             q10, [x20, x16]             // load filter values for idx 2
+        uxtl2           v6.4s, v6.8h
+        sxtl            v4.4s, v10.4h
+        sxtl2           v10.4s, v10.8h
+        mla             v1.4s, v5.4s, v12.4s       // multiplication of upper half for idx 1
+        mla             v2.4s, v11.4s, v4.4s       // multiplication of lower half for idx 2
+        ldr             q5, [x15, x16]             // load filter values for idx 3
+        uxtl            v11.4s, v7.4h
+        sxtl            v4.4s, v5.4h
+        mla             v2.4s, v6.4s, v10.4s       // multiplication of upper half for idx 2
+        uxtl2           v7.4s, v7.8h
+        sxtl2           v5.4s, v5.8h
+        mla             v3.4s, v11.4s, v4.4s      // multiplication of lower half for idx 3
+        sub             w0, w0, #8
+        cmp             w0, #8
+        mla             v3.4s, v7.4s, v5.4s       // multiplication of upper half for idx 3
+
+        add             x16, x16, #16               // advance filter values indexing
+
+        b.ge            2b
+
+        // 4 iterations left
+
+        sub             x17, x7, #8                 // step back to wrap up the filter pos for last 4 elements
+
+        ldr             d4, [x8]                    // load src values for idx 0
+        ldr             d7, [x12, x17]             // load filter values for idx 0
+        uxtl            v4.4s, v4.4h
+        sxtl            v7.4s, v7.4h
+        ldr             d5, [x9]                    // load src values for idx 1
+        mla             v0.4s, v4.4s, v7.4s       // multiplication of upper half for idx 0
+        ldr             d12, [x19, x17]             // load filter values for idx 1
+        uxtl            v5.4s, v5.4h
+        sxtl            v12.4s, v12.4h
+        ldr             d6, [x10]                   // load src values for idx 2
+        mla             v1.4s, v5.4s, v12.4s       // multiplication of upper half for idx 1
+        ldr             d10, [x20, x17]             // load filter values for idx 2
+        uxtl            v6.4s, v6.4h
+        sxtl            v10.4s, v10.4h
+        ldr             d7, [x11]                   // load src values for idx 3
+        ldr             d5, [x15, x17]             // load filter values for idx 3
+        mla             v2.4s, v6.4s, v10.4s       // multiplication of upper half for idx 2
+        uxtl            v7.4s, v7.4h
+        sxtl            v5.4s, v5.4h
+        addp            v0.4s, v0.4s, v1.4s
+        mla             v3.4s, v7.4s, v5.4s       // multiplication of upper half for idx 3
+        subs            w2, w2, #4
+        addp            v2.4s, v2.4s, v3.4s
+        addp            v0.4s, v0.4s, v2.4s
+        sshl            v0.4s, v0.4s, v8.4s
+        smin            v0.4s, v0.4s, v9.4s
+        xtn             v0.4h, v0.4s
+
+        st1             {v0.4h}, [x1], #8
+        add             x4, x4, x7, lsl #2
+        b.gt            1b
+
+        ldp             d8, d9, [sp, #0x10]
+        ldp             d10, d11, [sp, #0x20]
+        ldp             d12, d13, [sp, #0x30]
+        ldp             x19, x20, [sp], #0x40
+        ret
+endfunc
+
+function ff_hscale16to19_4_neon_asm, export=1
+        // w0               int shift
+        // x1               int32_t *dst
+        // w2               int dstW
+        // x3               const uint8_t *src // treat it as uint16_t *src
+        // x4               const uint16_t *filter
+        // x5               const int32_t *filterPos
+        // w6               int filterSize
+
+        stp             d8, d9, [sp, #-0x30]!
+        stp             d10, d11, [sp, #0x10]
+        stp             d12, d13, [sp, #0x20]
+
+        movi            v8.4s, #1
+        movi            v9.4s, #1
+        shl             v8.4s, v8.4s, #19
+        sub             v8.4s, v8.4s, v9.4s      // max allowed value
+        dup             v9.4s, w0                  // read shift
+        neg             v9.4s, v9.4s              // negate it, so it can be used in sshl (effectively shift right)
+
+        cmp             w2, #16
+        b.lt            2f // move to last block
+
+        ldp             w8, w9, [x5]                // filterPos[0], filterPos[1]
+        ldp             w10, w11, [x5, #8]          // filterPos[2], filterPos[3]
+        ldp             w12, w15, [x5, #16]         // filterPos[4], filterPos[5]
+        ldp             w16, w17, [x5, #24]         // filterPos[6], filterPos[7]
+        add             x5, x5, #32
+
+        // shift all filterPos left by one, as uint16_t will be read
+        lsl             x8, x8, #1
+        lsl             x9, x9, #1
+        lsl             x10, x10, #1
+        lsl             x11, x11, #1
+        lsl             x12, x12, #1
+        lsl             x15, x15, #1
+        lsl             x16, x16, #1
+        lsl             x17, x17, #1
+
+        // load src with given offset
+        ldr             x8,  [x3, w8,  uxtw]
+        ldr             x9,  [x3, w9,  uxtw]
+        ldr             x10, [x3, w10, uxtw]
+        ldr             x11, [x3, w11, uxtw]
+        ldr             x12, [x3, w12, uxtw]
+        ldr             x15, [x3, w15, uxtw]
+        ldr             x16, [x3, w16, uxtw]
+        ldr             x17, [x3, w17, uxtw]
+
+        sub             sp, sp, #64
+        // push src on stack so it can be loaded into vectors later
+        stp             x8, x9, [sp]
+        stp             x10, x11, [sp, #16]
+        stp             x12, x15, [sp, #32]
+        stp             x16, x17, [sp, #48]
+
+1:
+        ld4             {v0.8h, v1.8h, v2.8h, v3.8h}, [sp]
+        ld4             {v10.8h, v11.8h, v12.8h, v13.8h}, [x4], #64 // filter[0..7]
+
+        // Each of blocks does the following:
+        // Extend src and filter to 32 bits with uxtl and sxtl
+        // multiply or multiply and accumulate results
+        // Extending to 32 bits is necessary, as unit16_t values can't
+        // be represented as int16_t without type promotion.
+        uxtl            v4.4s, v0.4h
+        sxtl            v7.4s, v10.4h
+        uxtl2           v0.4s, v0.8h
+        mul             v5.4s, v4.4s, v7.4s
+        sxtl2           v10.4s, v10.8h
+        uxtl            v4.4s, v1.4h
+        mul             v6.4s, v0.4s, v10.4s
+
+        sxtl            v7.4s, v11.4h
+        uxtl2           v0.4s, v1.8h
+        mla             v5.4s, v7.4s, v4.4s
+        sxtl2           v10.4s, v11.8h
+        uxtl            v4.4s, v2.4h
+        mla             v6.4s, v10.4s, v0.4s
+
+        sxtl            v7.4s, v12.4h
+        uxtl2           v0.4s, v2.8h
+        mla             v5.4s, v7.4s, v4.4s
+        sxtl2           v10.4s, v12.8h
+        uxtl            v4.4s, v3.4h
+        mla             v6.4s, v10.4s, v0.4s
+
+        sxtl            v7.4s, v13.4h
+        uxtl2           v0.4s, v3.8h
+        mla             v5.4s, v7.4s, v4.4s
+        sxtl2           v10.4s, v13.8h
+        sub             w2, w2, #8
+        mla             v6.4s, v10.4s, v0.4s
+
+        sshl            v5.4s, v5.4s, v9.4s
+        sshl            v6.4s, v6.4s, v9.4s
+        smin            v5.4s, v5.4s, v8.4s
+        smin            v6.4s, v6.4s, v8.4s
+
+        st1             {v5.4s, v6.4s}, [x1], #32
+        cmp             w2, #16
+
+        // load filterPositions into registers for next iteration
+        ldp             w8, w9, [x5]                // filterPos[0], filterPos[1]
+        ldp             w10, w11, [x5, #8]          // filterPos[2], filterPos[3]
+        ldp             w12, w15, [x5, #16]         // filterPos[4], filterPos[5]
+        ldp             w16, w17, [x5, #24]         // filterPos[6], filterPos[7]
+        add             x5, x5, #32
+
+        lsl             x8, x8, #1
+        lsl             x9, x9, #1
+        lsl             x10, x10, #1
+        lsl             x11, x11, #1
+        lsl             x12, x12, #1
+        lsl             x15, x15, #1
+        lsl             x16, x16, #1
+        lsl             x17, x17, #1
+
+        ldr             x8,  [x3, w8,  uxtw]
+        ldr             x9,  [x3, w9,  uxtw]
+        ldr             x10, [x3, w10, uxtw]
+        ldr             x11, [x3, w11, uxtw]
+        ldr             x12, [x3, w12, uxtw]
+        ldr             x15, [x3, w15, uxtw]
+        ldr             x16, [x3, w16, uxtw]
+        ldr             x17, [x3, w17, uxtw]
+
+        stp             x8, x9, [sp]
+        stp             x10, x11, [sp, #16]
+        stp             x12, x15, [sp, #32]
+        stp             x16, x17, [sp, #48]
+
+        b.ge            1b
+
+        // here we make last iteration, without updating the registers
+        ld4             {v0.8h, v1.8h, v2.8h, v3.8h}, [sp]
+        ld4             {v10.8h, v11.8h, v12.8h, v13.8h}, [x4], #64
+
+        uxtl            v4.4s, v0.4h
+        sxtl            v7.4s, v10.4h
+        uxtl2           v0.4s, v0.8h
+        mul             v5.4s, v4.4s, v7.4s
+        sxtl2           v10.4s, v10.8h
+        uxtl            v4.4s, v1.4h
+        mul             v6.4s, v0.4s, v10.4s
+
+        sxtl            v7.4s, v11.4h
+        uxtl2           v0.4s, v1.8h
+        mla             v5.4s, v4.4s, v7.4s
+        sxtl2           v10.4s, v11.8h
+        uxtl            v4.4s, v2.4h
+        mla             v6.4s, v0.4s, v10.4s
+
+        sxtl            v7.4s, v12.4h
+        uxtl2           v0.4s, v2.8h
+        mla             v5.4s, v4.4s, v7.4s
+        sxtl2           v10.4s, v12.8h
+        uxtl            v4.4s, v3.4h
+        mla             v6.4s, v0.4s, v10.4s
+
+        sxtl            v7.4s, v13.4h
+        uxtl2           v0.4s, v3.8h
+        mla             v5.4s, v4.4s, v7.4s
+        sxtl2           v10.4s, v13.8h
+        subs            w2, w2, #8
+        mla             v6.4s, v0.4s, v10.4s
+
+        sshl            v5.4s, v5.4s, v9.4s
+        sshl            v6.4s, v6.4s, v9.4s
+
+        smin            v5.4s, v5.4s, v8.4s
+        smin            v6.4s, v6.4s, v8.4s
+
+        st1             {v5.4s, v6.4s}, [x1], #32
+        add             sp, sp, #64                 // restore stack
+        cbz             w2, 3f
+2:
+        ldr             w8, [x5], #4                // load filterPos
+        lsl             w8, w8, #1
+        add             x9, x3, w8, uxtw            // src + filterPos
+        ld1             {v0.4h}, [x9]               // load 4 * uint16_t
+        ld1             {v13.4h}, [x4], #8
+
+        uxtl            v0.4s, v0.4h
+        sxtl            v13.4s, v13.4h
+        subs            w2, w2, #1
+        mul             v5.4s, v0.4s, v13.4s
+        addv            s0, v5.4s
+        sshl            v0.4s, v0.4s, v9.4s
+        smin            v0.4s, v0.4s, v8.4s
+        st1             {v0.s}[0], [x1], #4
+        cbnz            w2, 2b                      // if iterations remain jump to beginning
+3:
+        ldp             d10, d11, [sp, #0x10]
+        ldp             d12, d13, [sp, #0x20]
+        ldp             d8, d9, [sp], #0x30
+        ret
+endfunc
+
+function ff_hscale16to19_X8_neon_asm, export=1
+        // w0               int shift
+        // x1               int32_t *dst
+        // w2               int dstW
+        // x3               const uint8_t *src // treat it as uint16_t *src
+        // x4               const uint16_t *filter
+        // x5               const int32_t *filterPos
+        // w6               int filterSize
+
+        stp             d8, d9, [sp, #-0x20]!
+        stp             d10, d11, [sp, #0x10]
+
+        movi            v8.4s, #1
+        movi            v9.4s, #1
+        shl             v8.4s, v8.4s, #19
+        sub             v8.4s, v8.4s, v9.4s
+        dup             v9.4s, w0
+        neg             v9.4s, v9.4s
+
+        sbfiz           x7, x6, #1, #32             // filterSize*2 (*2 because int16)
+1:      ldr             w8, [x5], #4                // filterPos[idx]
+        ldr             w10, [x5], #4               // filterPos[idx + 1]
+        lsl             w8, w8, #1
+        ldr             w11, [x5], #4               // filterPos[idx + 2]
+        ldr             w9, [x5], #4                // filterPos[idx + 3]
+        mov             x16, x4                     // filter0 = filter
+        lsl             w11, w11, #1
+        add             x12, x16, x7                // filter1 = filter0 + filterSize*2
+        lsl             w9, w9, #1
+        add             x15, x12, x7                // filter2 = filter1 + filterSize*2
+        lsl             w10, w10, #1
+        add             x4, x15, x7                 // filter3 = filter2 + filterSize*2
+        movi            v0.16b, #0                  // val sum part 1 (for dst[0])
+        movi            v1.16b, #0                  // val sum part 2 (for dst[1])
+        movi            v2.16b, #0                  // val sum part 3 (for dst[2])
+        movi            v3.16b, #0                  // val sum part 4 (for dst[3])
+        add             x17, x3, w8, uxtw           // srcp + filterPos[0]
+        add             x8,  x3, w10, uxtw          // srcp + filterPos[1]
+        add             x10, x3, w11, uxtw          // srcp + filterPos[2]
+        add             x11, x3, w9, uxtw           // srcp + filterPos[3]
+        mov             w15, w6                     // filterSize counter
+2:      ld1             {v4.8h}, [x17], #16         // srcp[filterPos[0] + {0..7}]
+        ld1             {v5.8h}, [x16], #16         // load 8x16-bit filter values, part 1
+        ld1             {v6.8h}, [x8], #16          // srcp[filterPos[1] + {0..7}]
+        ld1             {v7.8h}, [x12], #16         // load 8x16-bit at filter+filterSize
+        uxtl            v10.4s, v4.4h               // extend srcp lower half to 32 bits to preserve sign
+        sxtl            v11.4s, v5.4h               // extend filter lower half to 32 bits to match srcp size
+        uxtl2           v4.4s, v4.8h                // extend srcp upper half to 32 bits
+        mla             v0.4s, v10.4s, v11.4s       // multiply accumulate lower half of v4 * v5
+        sxtl2           v5.4s, v5.8h                // extend filter upper half to 32 bits
+        uxtl            v10.4s, v6.4h               // extend srcp lower half to 32 bits
+        mla             v0.4s, v4.4s, v5.4s         // multiply accumulate upper half of v4 * v5
+        sxtl            v11.4s, v7.4h               // extend filter lower half
+        uxtl2           v6.4s, v6.8h                // extend srcp upper half
+        sxtl2           v7.4s, v7.8h                // extend filter upper half
+        ld1             {v4.8h}, [x10], #16         // srcp[filterPos[2] + {0..7}]
+        mla             v1.4s, v10.4s, v11.4s       // v1 accumulates srcp[filterPos[1] + {0..3}] * filter[{0..3}]
+        ld1             {v5.8h}, [x15], #16         // load 8x16-bit at filter+2*filterSize
+        uxtl            v10.4s, v4.4h               // extend srcp lower half
+        sxtl            v11.4s, v5.4h               // extend filter lower half
+        uxtl2           v4.4s, v4.8h                // extend srcp upper half
+        sxtl2           v5.4s, v5.8h                // extend filter upper half
+        mla             v2.4s, v10.4s, v11.4s       // v2 accumulates srcp[filterPos[2] + {0..3}] * filter[{0..3}]
+        mla             v2.4s, v4.4s, v5.4s         // v2 accumulates srcp[filterPos[2] + {4..7}] * filter[{4..7}]
+        mla             v1.4s, v6.4s, v7.4s         // v1 accumulates srcp[filterPos[1] + {4..7}] * filter[{4..7}]
+        ld1             {v6.8h}, [x11], #16         // srcp[filterPos[3] + {0..7}]
+        ld1             {v7.8h}, [x4], #16          // load 8x16-bit at filter+3*filterSize
+        subs            w15, w15, #8                // j -= 8: processed 8/filterSize
+        uxtl            v10.4s, v6.4h               // extend srcp lower half
+        sxtl            v11.4s, v7.4h               // extend filter lower half
+        uxtl2           v6.4s, v6.8h                // extend srcp upper half
+        sxtl2           v7.4s, v7.8h                // extend filter upper half
+        mla             v3.4s, v10.4s, v11.4s       // v3 accumulates srcp[filterPos[3] + {0..3}] * filter[{0..3}]
+        mla             v3.4s, v6.4s, v7.4s         // v3 accumulates srcp[filterPos[3] + {4..7}] * filter[{4..7}]
+        b.gt            2b                          // inner loop if filterSize not consumed completely
+        addp            v0.4s, v0.4s, v1.4s         // part01 horizontal pair adding
+        addp            v2.4s, v2.4s, v3.4s         // part23 horizontal pair adding
+        addp            v0.4s, v0.4s, v2.4s         // part0123 horizontal pair adding
+        subs            w2, w2, #4                  // dstW -= 4
+        sshl            v0.4s, v0.4s, v9.4s         // shift right (effectively right, as shift is negative); overflow expected
+        smin            v0.4s, v0.4s, v8.4s         // apply min (do not use sqshl)
+        st1             {v0.4s}, [x1], #16          // write to destination part0123
+        b.gt            1b                          // loop until end of line
+        ldp             d10, d11, [sp, #0x10]
+        ldp             d8, d9, [sp], #0x20
+        ret
+endfunc
+
+function ff_hscale16to19_X4_neon_asm, export=1
+        // w0  int shift
+        // x1  int16_t *dst
+        // w2  int dstW
+        // x3  const uint8_t *src
+        // x4  const int16_t *filter
+        // x5  const int32_t *filterPos
+        // w6  int filterSize
+
+        stp             x19, x20, [sp, #-0x40]!
+        stp             d8, d9, [sp, #0x10]
+        stp             d10, d11, [sp, #0x20]
+        stp             d12, d13, [sp, #0x30]
+
+        movi            v2.4s, #1
+        movi            v1.4s, #1
+        shl             v2.4s, v2.4s, #19
+        sub             v9.4s, v2.4s, v1.4s      // max allowed value
+        dup             v1.4s, w0                  // read shift
+        neg             v8.4s, v1.4s              // negate it, so it can be used in sshl (effectively shift right)
+
+        lsl             w7, w6, #1
+1:
+        ldp             w8, w9, [x5]
+        ldp             w10, w11, [x5, #8]
+
+        movi            v0.16b, #0                 // initialize accumulator for idx + 0
+        movi            v1.16b, #0                 // initialize accumulator for idx + 1
+        movi            v2.16b, #0                 // initialize accumulator for idx + 2
+        movi            v3.16b, #0                 // initialize accumulator for idx + 3
+
+        mov             x12, x4                     // filter + 0
+        add             x19, x4, x7                 // filter + 1
+        add             x8, x3, x8, lsl #1          // srcp + filterPos 0
+        add             x20, x19, x7                // filter + 2
+        add             x9, x3, x9, lsl #1          // srcp + filterPos 1
+        add             x15, x20, x7                // filter + 3
+        add             x10, x3, x10, lsl #1        // srcp + filterPos 2
+        mov             w0, w6                      // save the filterSize to temporary variable
+        add             x11, x3, x11, lsl #1        // srcp + filterPos 3
+        add             x5, x5, #16                 // advance filter position
+        mov             x16, xzr                    // clear the register x16 used for offsetting the filter values
+
+2:
+        ldr             q4, [x8], #16               // load src values for idx 0
+        ldr             q5, [x9], #16               // load src values for idx 1
+        uxtl            v11.4s, v4.4h
+        uxtl2           v4.4s, v4.8h
+        ldr             q7, [x12, x16]             // load filter values for idx 0
+        ldr             q6, [x10], #16              // load src values for idx 2
+        sxtl            v10.4s, v7.4h
+        sxtl2           v7.4s, v7.8h
+        mla             v0.4s, v11.4s, v10.4s      // multiplication of lower half for idx 0
+        uxtl            v10.4s, v5.4h
+        uxtl2           v5.4s, v5.8h
+        mla             v0.4s, v4.4s, v7.4s       // multiplication of upper half for idx 0
+        ldr             q12, [x19, x16]             // load filter values for idx 1
+        ldr             q7, [x11], #16              // load src values for idx 3
+        uxtl            v11.4s, v6.4h
+        sxtl            v4.4s, v12.4h
+        sxtl2           v12.4s, v12.8h
+        mla             v1.4s, v10.4s, v4.4s       // multiplication of lower half for idx 1
+        ldr             q10, [x20, x16]             // load filter values for idx 2
+        uxtl2           v6.4s, v6.8h
+        sxtl            v4.4s, v10.4h
+        sxtl2           v10.4s, v10.8h
+        mla             v1.4s, v5.4s, v12.4s       // multiplication of upper half for idx 1
+        ldr             q5, [x15, x16]             // load filter values for idx 3
+        mla             v2.4s, v11.4s, v4.4s       // multiplication of lower half for idx 2
+        uxtl            v11.4s, v7.4h
+        sxtl            v4.4s, v5.4h
+        mla             v2.4s, v6.4s, v10.4s       // multiplication of upper half for idx 2
+        uxtl2           v7.4s, v7.8h
+        sxtl2           v5.4s, v5.8h
+        mla             v3.4s, v11.4s, v4.4s      // multiplication of lower half for idx 3
+        sub             w0, w0, #8
+        cmp             w0, #8
+        mla             v3.4s, v7.4s, v5.4s       // multiplication of upper half for idx 3
+
+        add             x16, x16, #16               // advance filter values indexing
+
+        b.ge            2b
+
+        // 4 iterations left
+
+        sub             x17, x7, #8                 // step back to wrap up the filter pos for last 4 elements
+
+        ldr             d4, [x8]                    // load src values for idx 0
+        ldr             d7, [x12, x17]             // load filter values for idx 0
+        uxtl            v4.4s, v4.4h
+        sxtl            v7.4s, v7.4h
+        ldr             d5, [x9]                    // load src values for idx 1
+        mla             v0.4s, v4.4s, v7.4s       // multiplication of upper half for idx 0
+        ldr             d12, [x19, x17]             // load filter values for idx 1
+        uxtl            v5.4s, v5.4h
+        sxtl            v12.4s, v12.4h
+        ldr             d6, [x10]                   // load src values for idx 2
+        mla             v1.4s, v5.4s, v12.4s       // multiplication of upper half for idx 1
+        ldr             d10, [x20, x17]             // load filter values for idx 2
+        uxtl            v6.4s, v6.4h
+        sxtl            v10.4s, v10.4h
+        ldr             d7, [x11]                   // load src values for idx 3
+        ldr             d5, [x15, x17]             // load filter values for idx 3
+        mla             v2.4s, v6.4s, v10.4s       // multiplication of upper half for idx 2
+        uxtl            v7.4s, v7.4h
+        sxtl            v5.4s, v5.4h
+        addp            v0.4s, v0.4s, v1.4s
+        mla             v3.4s, v7.4s, v5.4s       // multiplication of upper half for idx 3
+        subs            w2, w2, #4
+        addp            v2.4s, v2.4s, v3.4s
+        addp            v0.4s, v0.4s, v2.4s
+        sshl            v0.4s, v0.4s, v8.4s
+        smin            v0.4s, v0.4s, v9.4s
+
+        st1             {v0.4s}, [x1], #16
+        add             x4, x4, x7, lsl #2
+        b.gt            1b
+
+        ldp             d8, d9, [sp, #0x10]
+        ldp             d10, d11, [sp, #0x20]
+        ldp             d12, d13, [sp, #0x30]
+        ldp             x19, x20, [sp], #0x40
+        ret
+endfunc
diff --git a/libs/ffmpeg/libswscale/arm64ec/input.S b/libs/ffmpeg/libswscale/arm64ec/input.S
new file mode 100644
index 000000000000..a646c0eb6937
--- /dev/null
+++ b/libs/ffmpeg/libswscale/arm64ec/input.S
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2024 Zhao Zhili <quinkblack@foxmail.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/aarch64/asm.S"
+
+.macro rgb_to_yuv_load_rgb src, element=3
+    .if \element == 3
+        ld3             { v7.16b, v8.16b, v9.16b }, [\src]
+    .else
+        ld4             { v7.16b, v8.16b, v9.16b, v10.16b }, [\src]
+    .endif
+        uxtl            v10.8h, v7.8b             // v10: r
+        uxtl            v11.8h, v8.8b             // v11: g
+        uxtl            v12.8h, v9.8b             // v12: b
+        uxtl2           v13.8h, v7.16b            // v13: r
+        uxtl2           v14.8h, v8.16b            // v14: g
+        uxtl2           v15.8h, v9.16b            // v15: b
+.endm
+
+.macro argb_to_yuv_load_rgb src
+        ld4             { v7.16b, v8.16b, v9.16b, v10.16b }, [\src]
+        uxtl            v12.8h, v10.8b            // v12: b
+        uxtl2           v15.8h, v10.16b           // v15: b
+        uxtl            v10.8h, v8.8b             // v10: r
+        uxtl            v11.8h, v9.8b             // v11: g
+        uxtl2           v13.8h, v8.16b            // v13: r
+        uxtl2           v14.8h, v9.16b            // v14: g
+.endm
+
+.macro rgb_to_yuv_product r, g, b, dst, coef0, coef1, coef2, right_shift
+        mov             v7.16b, v6.16b                    // v7 = const_offset
+        smlal           v7.4s, \coef0\().4h, \r\().4h     // v7 += rx * r
+        smlal           v7.4s, \coef1\().4h, \g\().4h     // v7 += gx * g
+        smlal           v7.4s, \coef2\().4h, \b\().4h     // v7 += bx * b
+        sqshrn          \dst\().4h, v7.4s, \right_shift   // dst_lower_half = v7 >> right_shift
+        mov             v7.16b, v6.16b                    // v7 = const_offset
+        smlal2          v7.4s, \coef0\().8h, \r\().8h     // v7 += rx * r
+        smlal2          v7.4s, \coef1\().8h, \g\().8h     // v7 += gx * g
+        smlal2          v7.4s, \coef2\().8h, \b\().8h     // v7 += bx * b
+        sqshrn2         \dst\().8h, v7.4s, \right_shift   // dst_higher_half = v7 >> right_shift
+.endm
+
+.macro rgbToY_neon fmt_bgr, fmt_rgb, element, alpha_first=0
+function ff_\fmt_bgr\()ToY_neon, export=1
+        cmp             w4, #0                  // check width > 0
+        ldp             w12, w11, [x5]          // w12: ry, w11: gy
+        ldr             w10, [x5, #8]           // w10: by
+        b.gt            4f
+        ret
+endfunc
+
+function ff_\fmt_rgb\()ToY_neon, export=1
+        cmp             w4, #0                  // check width > 0
+        ldp             w10, w11, [x5]          // w10: ry, w11: gy
+        ldr             w12, [x5, #8]           // w12: by
+        b.gt            4f
+        ret
+4:
+        stp             d8, d9, [sp, #-0x40]!
+        stp             d10, d11, [sp, #0x10]
+        stp             d12, d13, [sp, #0x20]
+        stp             d14, d15, [sp, #0x30]
+
+        mov             w9, #256                // w9 = 1 << (RGB2YUV_SHIFT - 7)
+        movk            w9, #8, lsl #16         // w9 += 32 << (RGB2YUV_SHIFT - 1)
+        dup             v6.4s, w9               // w9: const_offset
+
+        cmp             w4, #16
+        dup             v0.8h, w10
+        dup             v1.8h, w11
+        dup             v2.8h, w12
+        b.lt            2f
+1:
+    .if \alpha_first
+        argb_to_yuv_load_rgb x1
+    .else
+        rgb_to_yuv_load_rgb x1, \element
+    .endif
+        rgb_to_yuv_product v10, v11, v12, v3, v0, v1, v2, #9
+        rgb_to_yuv_product v13, v14, v15, v4, v0, v1, v2, #9
+        sub             w4, w4, #16             // width -= 16
+        add             x1, x1, #(16*\element)
+        cmp             w4, #16                 // width >= 16 ?
+        stp             q3, q4, [x0], #32     // store to dst
+        b.ge            1b
+        cbz             x4, 3f
+2:
+    .if \alpha_first
+        ldrb            w15, [x1, #1]           // w15: r
+        ldrb            w16, [x1, #2]           // w16: g
+        ldrb            w17, [x1, #3]           // w17: b
+    .else
+        ldrb            w15, [x1]               // w15: r
+        ldrb            w16, [x1, #1]           // w16: g
+        ldrb            w17, [x1, #2]           // w17: b
+    .endif
+
+        smaddl          x15, w15, w10, x9       // x15 = ry * r + const_offset
+        smaddl          x15, w16, w11, x15      // x15 += gy * g
+        smaddl          x15, w17, w12, x15      // x15 += by * b
+        asr             w15, w15, #9            // x15 >>= 9
+        sub             w4, w4, #1              // width--
+        add             x1, x1, #\element
+        strh            w15, [x0], #2           // store to dst
+        cbnz            w4, 2b
+3:
+        ldp             d10, d11, [sp, #0x10]
+        ldp             d12, d13, [sp, #0x20]
+        ldp             d14, d15, [sp, #0x30]
+        ldp             d8, d9, [sp], #0x40
+        ret
+endfunc
+.endm
+
+rgbToY_neon bgr24, rgb24, element=3
+
+rgbToY_neon bgra32, rgba32, element=4
+
+rgbToY_neon abgr32, argb32, element=4, alpha_first=1
+
+.macro rgb_set_uv_coeff half
+    .if \half
+        mov             w9, #512
+        movk            w9, #128, lsl #16       // w9: const_offset
+    .else
+        mov             w9, #256
+        movk            w9, #64, lsl #16        // w9: const_offset
+    .endif
+        dup             v0.8h, w10
+        dup             v1.8h, w11
+        dup             v2.8h, w12
+        dup             v3.8h, w15
+        dup             v4.8h, w16
+        dup             v5.8h, w17
+        dup             v6.4s, w9
+.endm
+
+.macro rgb_load_add_half off_r1, off_r2, off_g1, off_g2, off_b1, off_b2
+        ldrb            w2, [x3, #\off_r1]     // w2: r1
+        ldrb            w4, [x3, #\off_r2]     // w4: r2
+        add             w2, w2, w4             // w2 = r1 + r2
+
+        ldrb            w4, [x3, #\off_g1]     // w4: g1
+        ldrb            w7, [x3, #\off_g2]     // w7: g2
+        add             w4, w4, w7             // w4 = g1 + g2
+
+        ldrb            w7, [x3, #\off_b1]     // w7: b1
+        ldrb            w8, [x3, #\off_b2]     // w8: b2
+        add             w7, w7, w8             // w7 = b1 + b2
+.endm
+
+.macro rgbToUV_half_neon fmt_bgr, fmt_rgb, element, alpha_first=0
+function ff_\fmt_bgr\()ToUV_half_neon, export=1
+        cmp             w5, #0          // check width > 0
+        b.gt            5f
+        ret
+5:
+        ldp             w12, w11, [x6, #12]
+        ldp             w10, w17, [x6, #20]
+        ldp             w16, w15, [x6, #28]
+        b               4f
+endfunc
+
+function ff_\fmt_rgb\()ToUV_half_neon, export=1
+        cmp             w5, #0          // check width > 0
+        b.gt            6f
+        ret
+6:
+        ldp             w10, w11, [x6, #12]     // w10: ru, w11: gu
+        ldp             w12, w15, [x6, #20]     // w12: bu, w15: rv
+        ldp             w16, w17, [x6, #28]     // w16: gv, w17: bv
+4:
+        stp             d8, d9, [sp, #-0x40]!
+        stp             d10, d11, [sp, #0x10]
+        stp             d12, d13, [sp, #0x20]
+        stp             d14, d15, [sp, #0x30]
+        cmp             w5, #8
+        rgb_set_uv_coeff half=1
+        b.lt            2f
+1:
+    .if \element == 3
+        ld3             { v7.16b, v8.16b, v9.16b }, [x3]
+    .else
+        ld4             { v7.16b, v8.16b, v9.16b, v10.16b }, [x3]
+    .endif
+    .if \alpha_first
+        uaddlp          v12.8h, v10.16b
+        uaddlp          v11.8h, v9.16b
+        uaddlp          v10.8h, v8.16b
+    .else
+        uaddlp          v10.8h, v7.16b         // v10: r
+        uaddlp          v11.8h, v8.16b         // v11: g
+        uaddlp          v12.8h, v9.16b         // v12: b
+    .endif
+
+        rgb_to_yuv_product v10, v11, v12, v8, v0, v1, v2, #10
+        rgb_to_yuv_product v10, v11, v12, v9, v3, v4, v5, #10
+        sub             w5, w5, #8              // width -= 8
+        add             x3, x3, #(16*\element)
+        cmp             w5, #8                  // width >= 8 ?
+        str             q8, [x0], #16          // store dst_u
+        str             q9, [x1], #16          // store dst_v
+        b.ge            1b
+        cbz             w5, 3f
+2:
+.if \alpha_first
+        rgb_load_add_half 1, 5, 2, 6, 3, 7
+.else
+    .if \element == 3
+        rgb_load_add_half 0, 3, 1, 4, 2, 5
+    .else
+        rgb_load_add_half 0, 4, 1, 5, 2, 6
+    .endif
+.endif
+
+        smaddl          x8, w2, w10, x9         // dst_u = ru * r + const_offset
+        smaddl          x8, w4, w11, x8         // dst_u += gu * g
+        smaddl          x8, w7, w12, x8         // dst_u += bu * b
+        asr             x8, x8, #10             // dst_u >>= 10
+        strh            w8, [x0], #2            // store dst_u
+
+        smaddl          x8, w2, w15, x9         // dst_v = rv * r + const_offset
+        smaddl          x8, w4, w16, x8         // dst_v += gv * g
+        smaddl          x8, w7, w17, x8         // dst_v += bv * b
+        asr             x8, x8, #10             // dst_v >>= 10
+        sub             w5, w5, #1
+        add             x3, x3, #(2*\element)
+        strh            w8, [x1], #2            // store dst_v
+        cbnz            w5, 2b
+3:
+        ldp             d10, d11, [sp, #0x10]
+        ldp             d12, d13, [sp, #0x20]
+        ldp             d14, d15, [sp, #0x30]
+        ldp             d8, d9, [sp], #0x40
+        ret
+endfunc
+.endm
+
+rgbToUV_half_neon bgr24, rgb24, element=3
+
+rgbToUV_half_neon bgra32, rgba32, element=4
+
+rgbToUV_half_neon abgr32, argb32, element=4, alpha_first=1
+
+.macro rgbToUV_neon fmt_bgr, fmt_rgb, element, alpha_first=0
+function ff_\fmt_bgr\()ToUV_neon, export=1
+        cmp             w5, #0                  // check width > 0
+        b.le            3f
+
+        ldp             w12, w11, [x6, #12]
+        ldp             w10, w17, [x6, #20]
+        ldp             w16, w15, [x6, #28]
+        b               4f
+endfunc
+
+function ff_\fmt_rgb\()ToUV_neon, export=1
+        cmp             w5, #0                  // check width > 0
+        b.le            3f
+
+        ldp             w10, w11, [x6, #12]     // w10: ru, w11: gu
+        ldp             w12, w15, [x6, #20]     // w12: bu, w15: rv
+        ldp             w16, w17, [x6, #28]     // w16: gv, w17: bv
+4:
+        cmp             w5, #16
+        rgb_set_uv_coeff half=0
+        b.lt            2f
+1:
+    .if \alpha_first
+        argb_to_yuv_load_rgb x3
+    .else
+        rgb_to_yuv_load_rgb x3, \element
+    .endif
+        rgb_to_yuv_product v10, v11, v12, v8, v0, v1, v2, #9
+        rgb_to_yuv_product v13, v14, v15, v9, v0, v1, v2, #9
+        stp             q8, q9, [x0], #32     // store to dst_u
+
+        rgb_to_yuv_product v10, v11, v12, v8, v3, v4, v5, #9
+        rgb_to_yuv_product v13, v14, v15, v9, v3, v4, v5, #9
+        sub             w5, w5, #16
+        add             x3, x3, #(16*\element)
+        cmp             w5, #16
+        stp             q8, q9, [x1], #32     // store to dst_v
+        b.ge            1b
+        cbz             w5, 3f
+2:
+    .if \alpha_first
+        ldrb            w2, [x3, #1]            // w2: r
+        ldrb            w6, [x3, #2]            // w6: g
+        ldrb            w4, [x3, #3]            // w4: b
+    .else
+        ldrb            w2, [x3]                // w2: r
+        ldrb            w6, [x3, #1]            // w6: g
+        ldrb            w4, [x3, #2]            // w4: b
+    .endif
+
+        smaddl          x8, w2, w10, x9         // x8 = ru * r + const_offset
+        smaddl          x8, w6, w11, x8         // x8 += gu * g
+        smaddl          x8, w4, w12, x8         // x8 += bu * b
+        asr             w8, w8, #9              // x8 >>= 9
+        strh            w8, [x0], #2            // store to dst_u
+
+        smaddl          x8, w2, w15, x9         // x8 = rv * r + const_offset
+        smaddl          x8, w6, w16, x8         // x8 += gv * g
+        smaddl          x8, w4, w17, x8         // x8 += bv * b
+        asr             w8, w8, #9              // x8 >>= 9
+        sub             w5, w5, #1              // width--
+        add             x3, x3, #\element
+        strh            w8, [x1], #2            // store to dst_v
+        cbnz            w5, 2b
+3:
+        ret
+endfunc
+.endm
+
+rgbToUV_neon bgr24, rgb24, element=3
+
+rgbToUV_neon bgra32, rgba32, element=4
+
+rgbToUV_neon abgr32, argb32, element=4, alpha_first=1
+
+#if HAVE_DOTPROD
+ENABLE_DOTPROD
+
+function ff_bgra32ToY_neon_dotprod, export=1
+        cmp             w4, #0                  // check width > 0
+        ldp             w12, w11, [x5]          // w12: ry, w11: gy
+        ldr             w10, [x5, #8]           // w10: by
+        b.gt            4f
+        ret
+endfunc
+
+function ff_rgba32ToY_neon_dotprod, export=1
+        cmp             w4, #0                  // check width > 0
+        ldp             w10, w11, [x5]          // w10: ry, w11: gy
+        ldr             w12, [x5, #8]           // w12: by
+        b.gt            4f
+        ret
+4:
+        stp             d8, d9, [sp, #-0x20]!
+        stp             d10, d11, [sp, #0x10]
+
+        mov             w9, #256                // w9 = 1 << (RGB2YUV_SHIFT - 7)
+        movk            w9, #8, lsl #16         // w9 += 32 << (RGB2YUV_SHIFT - 1)
+        dup             v6.4s, w9               // w9: const_offset
+
+        cmp             w4, #16
+        mov             w7, w10
+        bfi             w7, w11, 8, 8           // the bfi instructions are used to assemble
+        bfi             w7, w12, 16, 8          // 4 byte r,g,b,0 mask to be then used by udot.
+        dup             v0.4s, w7               // v0 holds the lower byte of each coefficient
+
+        lsr             w6, w10, #8
+        lsr             w7, w11, #8
+        lsr             w8, w12, #8
+
+        bfi             w6, w7, 8, 8
+        bfi             w6, w8, 16, 8
+        dup             v1.4s, w6               // v1 holds the upper byte of each coefficient
+        b.lt            2f
+1:
+        ld1             { v8.16b, v9.16b, v10.16b, v11.16b }, [x1], #64
+        sub             w4, w4, #16             // width -= 16
+
+        mov             v2.16b, v6.16b
+        mov             v3.16b, v6.16b
+        mov             v4.16b, v6.16b
+        mov             v5.16b, v6.16b
+        cmp             w4, #16                 // width >= 16 ?
+
+        udot            v2.4s, v8.16b, v0.16b
+        udot            v3.4s, v9.16b, v0.16b
+        udot            v4.4s, v10.16b, v0.16b
+        udot            v5.4s, v11.16b, v0.16b
+
+        ushr            v2.4s, v2.4s, #8
+        ushr            v3.4s, v3.4s, #8
+        ushr            v4.4s, v4.4s, #8
+        ushr            v5.4s, v5.4s, #8
+
+        udot            v2.4s, v8.16b, v1.16b
+        udot            v3.4s, v9.16b, v1.16b
+        udot            v4.4s, v10.16b, v1.16b
+        udot            v5.4s, v11.16b, v1.16b
+
+        sqshrn          v8.4h, v2.4s, #1
+        sqshrn2         v8.8h, v3.4s, #1
+        sqshrn          v9.4h, v4.4s, #1
+        sqshrn2         v9.8h, v5.4s, #1
+
+        stp             q8, q9, [x0], #32     // store to dst
+        b.ge            1b
+        cbz             x4, 3f
+2:
+        ldrb            w15, [x1]               // w15: r
+        ldrb            w16, [x1, #1]           // w16: g
+        ldrb            w17, [x1, #2]           // w17: b
+
+        smaddl          x15, w15, w10, x9       // x15 = ry * r + const_offset
+        smaddl          x15, w16, w11, x15      // x15 += gy * g
+        smaddl          x15, w17, w12, x15      // x15 += by * b
+        asr             w15, w15, #9            // x15 >>= 9
+        sub             w4, w4, #1              // width--
+        add             x1, x1, #4
+        strh            w15, [x0], #2           // store to dst
+        cbnz            w4, 2b
+3:
+        ldp             d10, d11, [sp, #0x10]
+        ldp             d8, d9, [sp], #0x20
+        ret
+endfunc
+
+DISABLE_DOTPROD
+#endif
diff --git a/libs/ffmpeg/libswscale/arm64ec/output.S b/libs/ffmpeg/libswscale/arm64ec/output.S
new file mode 100644
index 000000000000..85b82f093c42
--- /dev/null
+++ b/libs/ffmpeg/libswscale/arm64ec/output.S
@@ -0,0 +1,655 @@
+/*
+ * Copyright (c) 2016 Clément Bœsch <clement stupeflix.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/aarch64/asm.S"
+
+function ff_yuv2planeX_10_neon, export=1
+// x0 = filter (int16_t*)
+// w1 = filterSize
+// x2 = src (int16_t**)
+// x3 = dest (uint16_t*)
+// w4 = dstW
+// w5 = big_endian
+// w6 = output_bits
+        stp             x19, x20, [sp, #-0x50]!
+        stp             d8, d9, [sp, #0x10]
+        stp             d10, d11, [sp, #0x20]
+        stp             d12, d13, [sp, #0x30]
+        stp             d14, d15, [sp, #0x40]
+
+        mov             w8, #27
+        sub             w8, w8, w6                      // shift = 11 + 16 - output_bits
+
+        sub             w9, w8, #1
+        mov             w10, #1
+        lsl             w9, w10, w9                     // val = 1 << (shift - 1)
+
+        dup             v1.4s, w9
+        dup             v2.4s, w9                       // Create vectors with val
+
+        neg             w16, w8
+        dup             v12.4s, w16                     // Create (-shift) vector for right shift
+
+        mov             w10, #1
+        lsl             w10, w10, w6
+        sub             w10, w10, #1                    // (1U << output_bits) - 1
+        dup             v13.8h, w10                     // Create Clip vector for upper bound
+
+        mov             x7, #0                          // i = 0
+
+1:
+        cmp             w4, #16                         // Process 16-pixels if available
+        blt             4f
+
+        mov             v3.16b, v1.16b
+        mov             v4.16b, v2.16b
+        mov             v5.16b, v1.16b
+        mov             v6.16b, v2.16b
+
+        mov             w11, w1                         // tmpfilterSize = filterSize
+        mov             x12, x2                         // srcp = src
+        mov             x19, x0                         // filterp = filter
+
+2:                                                      // Filter loop
+        ldp             x20, x15, [x12], #16            // get 2 pointers: src[j] and src[j+1]
+        ldr             s7, [x19], #4                   // load filter coefficients
+        add             x20, x20, x7, lsl #1
+        add             x15, x15, x7, lsl #1
+        ld1             {v8.8h, v9.8h}, [x20]
+        ld1             {v10.8h, v11.8h}, [x15]
+
+        subs            w11, w11, #2                    // tmpfilterSize -= 2
+
+        smlal           v3.4s,  v8.4h, v7.h[0]         // Multiply-accumulate
+        smlal2          v4.4s,  v8.8h, v7.h[0]
+        smlal           v5.4s,  v9.4h, v7.h[0]
+        smlal2          v6.4s,  v9.8h, v7.h[0]
+
+        smlal           v3.4s,  v10.4h, v7.h[1]
+        smlal2          v4.4s,  v10.8h, v7.h[1]
+        smlal           v5.4s,  v11.4h, v7.h[1]
+        smlal2          v6.4s,  v11.8h, v7.h[1]
+
+        b.gt            2b                              // continue filter loop
+
+        sshl            v3.4s,  v3.4s, v12.4s           // Shift results
+        sshl            v4.4s,  v4.4s, v12.4s
+        sshl            v5.4s,  v5.4s, v12.4s
+        sshl            v6.4s,  v6.4s, v12.4s
+
+        sqxtun          v14.4h, v3.4s                   // Narrow and clamp to 0
+        sqxtun2         v14.8h, v4.4s
+        sqxtun          v15.4h, v5.4s
+        sqxtun2         v15.8h, v6.4s
+
+        umin            v14.8h, v14.8h, v13.8h
+        umin            v15.8h, v15.8h, v13.8h
+
+        cbz             w5, 3f                          // Check if big endian
+        rev16           v14.16b, v14.16b
+        rev16           v15.16b, v15.16b                // Swap bits for big endian
+3:
+        st1             {v14.8h, v15.8h}, [x3], #32
+
+        subs            w4, w4, #16                     // dstW = dstW - 16
+        add             x7, x7, #16                     // i = i + 16
+        b               1b                              // Continue loop
+
+4:
+        cmp             w4, #8                          // Process 8-pixels if available
+        blt             8f
+5:
+        mov             v3.16b, v1.16b
+        mov             v4.16b, v2.16b
+
+        mov             w11, w1                         // tmpfilterSize = filterSize
+        mov             x12, x2                         // srcp = src
+        mov             x19, x0                         // filterp = filter
+
+6:                                                      // Filter loop
+        ldp             x20, x15, [x12], #16
+        ldr             s7, [x19], #4
+        add             x20, x20, x7, lsl #1
+        add             x15, x15, x7, lsl #1
+        ld1             {v5.8h}, [x20]
+        ld1             {v6.8h}, [x15]
+
+        subs            w11, w11, #2                    // tmpfilterSize -= 2
+
+        smlal           v3.4s, v5.4h, v7.h[0]           // Multiply-accumulate
+        smlal2          v4.4s, v5.8h, v7.h[0]
+        smlal           v3.4s, v6.4h, v7.h[1]
+        smlal2          v4.4s, v6.8h, v7.h[1]
+
+        b.gt            6b                              // loop until filterSize consumed
+
+        sshl            v3.4s, v3.4s, v12.4s            // Shift results
+        sshl            v4.4s, v4.4s, v12.4s
+
+        sqxtun          v15.4h, v3.4s                   // Narrow and clamp to 0
+        sqxtun2         v15.8h, v4.4s
+
+        umin            v15.8h, v15.8h, v13.8h
+
+        cbz             w5, 7f                          // Check if big endian
+        rev16           v15.16b, v15.16b
+
+7:
+        st1             {v15.8h}, [x3], #16             // Store 8 pixels
+
+        subs            w4, w4, #8                      // dstW = dstW - 8
+        add             x7, x7, #8                      // i = i + 8
+8:
+        cbz             w4, 12f                         // Scalar loop for remaining pixels
+9:
+        mov             w11, w1                         // tmpfilterSize = filterSize
+        mov             x12, x2                         // srcp = src
+        mov             x19, x0                         // filterp = filter
+        sxtw            x9, w9
+        mov             x17, x9
+
+10:                                                     // Filter loop
+        ldr             x20, [x12], #8                  // Load src pointer
+        ldrsh           w15, [x19], #2                  // Load filter coefficient
+        add             x20, x20, x7, lsl #1            // Add pixel offset
+        ldrh            w16, [x20]
+
+        sxtw            x16, w16
+        sxtw            x15, w15
+        madd            x17, x16, x15, x17
+
+        subs            w11, w11, #1                    // tmpfilterSize -= 1
+        b.gt            10b                             // loop until filterSize consumed
+
+        sxtw            x8, w8
+        asr             x17, x17, x8
+        cmp             x17, #0
+        csel            x17, x17, xzr, ge               // Clamp to 0 if negative
+
+        sxtw            x10, w10
+        cmp             x17, x10
+        csel            x17, x10, x17, gt               // Clamp to max if greater than max
+
+        cbz             w5, 11f                         // Check if big endian
+        rev16           x17, x17                        // Swap bits for big endian
+11:
+        strh            w17, [x3], #2
+
+        subs            w4, w4, #1                      // dstW = dstW - 1
+        add             x7, x7, #1                      // i = i + 1
+        b.gt            9b                              // Loop if more pixels
+
+12:
+        ldp             d8, d9, [sp, #0x10]
+        ldp             d10, d11, [sp, #0x20]
+        ldp             d12, d13, [sp, #0x30]
+        ldp             d14, d15, [sp, #0x40]
+        ldp             x19, x20, [sp], #0x50
+        ret
+endfunc
+
+function ff_yuv2planeX_8_neon, export=1
+// x0 - const int16_t *filter,
+// x1 - int filterSize,
+// x2 - const int16_t **src,
+// x3 - uint8_t *dest,
+// w4 - int dstW,
+// x5 - const uint8_t *dither,
+// w6 - int offset
+
+        ld1             {v0.8b}, [x5]                   // load 8x8-bit dither
+        and             w6, w6, #7
+        cbz             w6, 1f                          // check if offsetting present
+        ext             v0.8b, v0.8b, v0.8b, #3         // honor offsetting which can be 0 or 3 only
+1:      uxtl            v0.8h, v0.8b                    // extend dither to 16-bit
+        ushll           v1.4s, v0.4h, #12               // extend dither to 32-bit with left shift by 12 (part 1)
+        ushll2          v2.4s, v0.8h, #12               // extend dither to 32-bit with left shift by 12 (part 2)
+        cmp             w1, #8                          // if filterSize == 8, branch to specialized version
+        b.eq            6f
+        cmp             w1, #4                          // if filterSize == 4, branch to specialized version
+        b.eq            8f
+        cmp             w1, #2                          // if filterSize == 2, branch to specialized version
+        b.eq            10f
+
+// The filter size does not match of the of specialized implementations. It is either even or odd. If it is even
+// then use the first section below.
+        mov             x7, #0                          // i = 0
+        tbnz            w1, #0, 4f                      // if filterSize % 2 != 0 branch to specialized version
+// fs % 2 == 0
+2:      mov             v3.16b, v1.16b                  // initialize accumulator part 1 with dithering value
+        mov             v4.16b, v2.16b                  // initialize accumulator part 2 with dithering value
+        mov             w8, w1                          // tmpfilterSize = filterSize
+        mov             x9, x2                          // srcp    = src
+        mov             x10, x0                         // filterp = filter
+3:      ldp             x11, x12, [x9], #16             // get 2 pointers: src[j] and src[j+1]
+        ldr             s7, [x10], #4                   // read 2x16-bit coeff X and Y at filter[j] and filter[j+1]
+        add             x11, x11, x7, lsl #1            // &src[j  ][i]
+        add             x12, x12, x7, lsl #1            // &src[j+1][i]
+        ld1             {v5.8h}, [x11]                  // read 8x16-bit @ src[j  ][i + {0..7}]: A,B,C,D,E,F,G,H
+        ld1             {v6.8h}, [x12]                  // read 8x16-bit @ src[j+1][i + {0..7}]: I,J,K,L,M,N,O,P
+        smlal           v3.4s, v5.4h, v7.h[0]           // val0 += {A,B,C,D} * X
+        smlal2          v4.4s, v5.8h, v7.h[0]           // val1 += {E,F,G,H} * X
+        smlal           v3.4s, v6.4h, v7.h[1]           // val0 += {I,J,K,L} * Y
+        smlal2          v4.4s, v6.8h, v7.h[1]           // val1 += {M,N,O,P} * Y
+        subs            w8, w8, #2                      // tmpfilterSize -= 2
+        b.gt            3b                              // loop until filterSize consumed
+
+        sqshrun         v3.4h, v3.4s, #16               // clip16(val0>>16)
+        sqshrun2        v3.8h, v4.4s, #16               // clip16(val1>>16)
+        uqshrn          v3.8b, v3.8h, #3                // clip8(val>>19)
+        st1             {v3.8b}, [x3], #8               // write to destination
+        subs            w4, w4, #8                      // dstW -= 8
+        add             x7, x7, #8                      // i += 8
+        b.gt            2b                              // loop until width consumed
+        ret
+
+// If filter size is odd (most likely == 1), then use this section.
+// fs % 2 != 0
+4:      mov             v3.16b, v1.16b                  // initialize accumulator part 1 with dithering value
+        mov             v4.16b, v2.16b                  // initialize accumulator part 2 with dithering value
+        mov             w8, w1                          // tmpfilterSize = filterSize
+        mov             x9, x2                          // srcp    = src
+        mov             x10, x0                         // filterp = filter
+5:      ldr             x11, [x9], #8                   // get 1 pointer: src[j]
+        ldr             h6, [x10], #2                   // read 1 16 bit coeff X at filter[j]
+        add             x11, x11, x7, lsl #1            // &src[j  ][i]
+        ld1             {v5.8h}, [x11]                  // read 8x16-bit @ src[j  ][i + {0..7}]: A,B,C,D,E,F,G,H
+        smlal           v3.4s, v5.4h, v6.h[0]           // val0 += {A,B,C,D} * X
+        smlal2          v4.4s, v5.8h, v6.h[0]           // val1 += {E,F,G,H} * X
+        subs            w8, w8, #1                      // tmpfilterSize -= 2
+        b.gt            5b                              // loop until filterSize consumed
+
+        sqshrun         v3.4h, v3.4s, #16               // clip16(val0>>16)
+        sqshrun2        v3.8h, v4.4s, #16               // clip16(val1>>16)
+        uqshrn          v3.8b, v3.8h, #3                // clip8(val>>19)
+        st1             {v3.8b}, [x3], #8               // write to destination
+        subs            w4, w4, #8                      // dstW -= 8
+        add             x7, x7, #8                      // i += 8
+        b.gt            4b                              // loop until width consumed
+        ret
+
+6:      // fs=8
+        stp             d8, d9, [sp, #-48]!
+        stp             d10, d11, [sp, #16]
+        stp             d12, d13, [sp, #32]
+
+        ldp             x5, x6, [x2]                    // load 2 pointers: src[j  ] and src[j+1]
+        ldp             x7, x9, [x2, #16]               // load 2 pointers: src[j+2] and src[j+3]
+        ldp             x10, x11, [x2, #32]             // load 2 pointers: src[j+4] and src[j+5]
+        ldp             x12, x15, [x2, #48]             // load 2 pointers: src[j+6] and src[j+7]
+
+        // load 8x16-bit values for filter[j], where j=0..7
+        ld1             {v6.8h}, [x0]
+7:
+        mov             v3.16b, v1.16b                  // initialize accumulator part 1 with dithering value
+        mov             v4.16b, v2.16b                  // initialize accumulator part 2 with dithering value
+
+        ld1             {v5.8h}, [x5], #16             // load 8x16-bit values for src[j + 0][i + {0..7}]
+        ld1             {v7.8h}, [x6], #16             // load 8x16-bit values for src[j + 1][i + {0..7}]
+        ld1             {v8.8h}, [x7], #16             // load 8x16-bit values for src[j + 2][i + {0..7}]
+        ld1             {v9.8h}, [x9], #16             // load 8x16-bit values for src[j + 3][i + {0..7}]
+        ld1             {v10.8h}, [x10], #16            // load 8x16-bit values for src[j + 4][i + {0..7}]
+        ld1             {v11.8h}, [x11], #16            // load 8x16-bit values for src[j + 5][i + {0..7}]
+        ld1             {v12.8h}, [x12], #16            // load 8x16-bit values for src[j + 6][i + {0..7}]
+        ld1             {v13.8h}, [x15], #16            // load 8x16-bit values for src[j + 7][i + {0..7}]
+
+        smlal           v3.4s, v5.4h, v6.h[0]          // val0 += src[0][i + {0..3}] * filter[0]
+        smlal2          v4.4s, v5.8h, v6.h[0]          // val1 += src[0][i + {4..7}] * filter[0]
+        smlal           v3.4s, v7.4h, v6.h[1]          // val0 += src[1][i + {0..3}] * filter[1]
+        smlal2          v4.4s, v7.8h, v6.h[1]          // val1 += src[1][i + {4..7}] * filter[1]
+        smlal           v3.4s, v8.4h, v6.h[2]          // val0 += src[2][i + {0..3}] * filter[2]
+        smlal2          v4.4s, v8.8h, v6.h[2]          // val1 += src[2][i + {4..7}] * filter[2]
+        smlal           v3.4s, v9.4h, v6.h[3]          // val0 += src[3][i + {0..3}] * filter[3]
+        smlal2          v4.4s, v9.8h, v6.h[3]          // val1 += src[3][i + {4..7}] * filter[3]
+        smlal           v3.4s, v10.4h, v6.h[4]          // val0 += src[4][i + {0..3}] * filter[4]
+        smlal2          v4.4s, v10.8h, v6.h[4]          // val1 += src[4][i + {4..7}] * filter[4]
+        smlal           v3.4s, v11.4h, v6.h[5]          // val0 += src[5][i + {0..3}] * filter[5]
+        smlal2          v4.4s, v11.8h, v6.h[5]          // val1 += src[5][i + {4..7}] * filter[5]
+        smlal           v3.4s, v12.4h, v6.h[6]          // val0 += src[6][i + {0..3}] * filter[6]
+        smlal2          v4.4s, v12.8h, v6.h[6]          // val1 += src[6][i + {4..7}] * filter[6]
+        smlal           v3.4s, v13.4h, v6.h[7]          // val0 += src[7][i + {0..3}] * filter[7]
+        smlal2          v4.4s, v13.8h, v6.h[7]          // val1 += src[7][i + {4..7}] * filter[7]
+
+        sqshrun         v3.4h, v3.4s, #16               // clip16(val0>>16)
+        sqshrun2        v3.8h, v4.4s, #16               // clip16(val1>>16)
+        uqshrn          v3.8b, v3.8h, #3                // clip8(val>>19)
+        subs            w4, w4, #8                      // dstW -= 8
+        st1             {v3.8b}, [x3], #8               // write to destination
+        b.gt            7b                              // loop until width consumed
+
+        ldp             d10, d11, [sp, #16]
+        ldp             d12, d13, [sp, #32]
+        ldp             d8, d9, [sp], #48
+        ret
+
+8:      // fs=4
+        stp             d8, d9, [sp, #-16]!
+        ldp             x5, x6, [x2]                    // load 2 pointers: src[j  ] and src[j+1]
+        ldp             x7, x9, [x2, #16]               // load 2 pointers: src[j+2] and src[j+3]
+
+        // load 4x16-bit values for filter[j], where j=0..3 and replicated across lanes
+        ld1             {v6.4h}, [x0]
+9:
+        mov             v3.16b, v1.16b                  // initialize accumulator part 1 with dithering value
+        mov             v4.16b, v2.16b                  // initialize accumulator part 2 with dithering value
+
+        ld1             {v5.8h}, [x5], #16             // load 8x16-bit values for src[j + 0][i + {0..7}]
+        ld1             {v7.8h}, [x6], #16             // load 8x16-bit values for src[j + 1][i + {0..7}]
+        ld1             {v8.8h}, [x7], #16             // load 8x16-bit values for src[j + 2][i + {0..7}]
+        ld1             {v9.8h}, [x9], #16             // load 8x16-bit values for src[j + 3][i + {0..7}]
+
+        smlal           v3.4s, v5.4h, v6.h[0]          // val0 += src[0][i + {0..3}] * filter[0]
+        smlal2          v4.4s, v5.8h, v6.h[0]          // val1 += src[0][i + {4..7}] * filter[0]
+        smlal           v3.4s, v7.4h, v6.h[1]          // val0 += src[1][i + {0..3}] * filter[1]
+        smlal2          v4.4s, v7.8h, v6.h[1]          // val1 += src[1][i + {4..7}] * filter[1]
+        smlal           v3.4s, v8.4h, v6.h[2]          // val0 += src[2][i + {0..3}] * filter[2]
+        smlal2          v4.4s, v8.8h, v6.h[2]          // val1 += src[2][i + {4..7}] * filter[2]
+        smlal           v3.4s, v9.4h, v6.h[3]          // val0 += src[3][i + {0..3}] * filter[3]
+        smlal2          v4.4s, v9.8h, v6.h[3]          // val1 += src[3][i + {4..7}] * filter[3]
+
+        sqshrun         v3.4h, v3.4s, #16               // clip16(val0>>16)
+        sqshrun2        v3.8h, v4.4s, #16               // clip16(val1>>16)
+        uqshrn          v3.8b, v3.8h, #3                // clip8(val>>19)
+        st1             {v3.8b}, [x3], #8               // write to destination
+        subs            w4, w4, #8                      // dstW -= 8
+        b.gt            9b                              // loop until width consumed
+        ldp             d8, d9, [sp], #16
+        ret
+
+10:     // fs=2
+        ldp             x5, x6, [x2]                    // load 2 pointers: src[j  ] and src[j+1]
+
+        // load 2x16-bit values for filter[j], where j=0..1 and replicated across lanes
+        ldr             s6, [x0]
+11:
+        mov             v3.16b, v1.16b                  // initialize accumulator part 1 with dithering value
+        mov             v4.16b, v2.16b                  // initialize accumulator part 2 with dithering value
+
+        ld1             {v5.8h}, [x5], #16             // load 8x16-bit values for src[j + 0][i + {0..7}]
+        ld1             {v7.8h}, [x6], #16             // load 8x16-bit values for src[j + 1][i + {0..7}]
+
+        smlal           v3.4s, v5.4h, v6.h[0]          // val0 += src[0][i + {0..3}] * filter[0]
+        smlal2          v4.4s, v5.8h, v6.h[0]          // val1 += src[0][i + {4..7}] * filter[0]
+        smlal           v3.4s, v7.4h, v6.h[1]          // val0 += src[1][i + {0..3}] * filter[1]
+        smlal2          v4.4s, v7.8h, v6.h[1]          // val1 += src[1][i + {4..7}] * filter[1]
+
+        sqshrun         v3.4h, v3.4s, #16               // clip16(val0>>16)
+        sqshrun2        v3.8h, v4.4s, #16               // clip16(val1>>16)
+        uqshrn          v3.8b, v3.8h, #3                // clip8(val>>19)
+        st1             {v3.8b}, [x3], #8               // write to destination
+        subs            w4, w4, #8                      // dstW -= 8
+        b.gt            11b                             // loop until width consumed
+        ret
+endfunc
+
+function ff_yuv2plane1_8_neon, export=1
+// x0 - const int16_t *src,
+// x1 - uint8_t *dest,
+// w2 - int dstW,
+// x3 - const uint8_t *dither,
+// w4 - int offset
+        ld1             {v0.8b}, [x3]                   // load 8x8-bit dither
+        and             w4, w4, #7
+        cbz             w4, 1f                          // check if offsetting present
+        ext             v0.8b, v0.8b, v0.8b, #3         // honor offsetting which can be 0 or 3 only
+1:
+        uxtl            v0.8h, v0.8b                    // extend dither to 32-bit
+2:
+        ld1             {v3.8h}, [x0], #16              // read 8x16-bit @ src[j  ][i + {0..7}]: A,B,C,D,E,F,G,H
+        subs            w2, w2, #8                      // dstW -= 8
+        shadd           v1.8h, v0.8h, v3.8h             // v1 = (v0 + v3) >> 1
+        sqshrun         v2.8b, v1.8h, #6                // clip_uint8(v1 >> 6)
+
+        st1             {v2.8b}, [x1], #8               // write to destination
+        b.gt            2b                              // loop until width consumed
+        ret
+endfunc
+
+function ff_yuv2nv12cX_neon_asm, export=1
+// w0 - isSwapped
+// x1 - uint8_t *chrDither
+// x2 - int16_t *chrFilter
+// x3 - int chrFilterSize
+// x4 - int16_t **chrUSrc
+// x5 - int16_t **chrVSrc
+// x6 - uint8_t *dest
+// x7 - int chrDstW
+
+        stp             x19, x20, [sp, #-0x70]!
+        stp             x21, x22, [sp, #0x10]
+        stp             x25, x26, [sp, #0x20]
+        stp             d8, d9, [sp, #0x30]
+        stp             d10, d11, [sp, #0x40]
+        stp             d12, d13, [sp, #0x50]
+        stp             d14, d15, [sp, #0x60]
+
+        ld1             {v0.8b}, [x1]                   // chrDither[0..7]
+        ext             v1.8b, v0.8b, v0.8b, #3         // Rotate for V: (i+3)&7
+
+        uxtl            v0.8h, v0.8b
+        uxtl            v1.8h, v1.8b
+
+        mov             x8, #0                          // i = 0
+1:
+        cmp             w7, #16
+        blt             5f
+
+        ushll           v8.4s, v0.4h, #12               // U dither low
+        ushll2          v9.4s, v0.8h, #12               // U dither high
+        ushll           v10.4s, v1.4h, #12               // V dither low
+        ushll2          v11.4s, v1.8h, #12               // V dither high
+
+        ushll           v12.4s, v0.4h, #12               // U dither low
+        ushll2          v13.4s, v0.8h, #12               // U dither high
+        ushll           v14.4s, v1.4h, #12               // V dither low
+        ushll2          v15.4s, v1.8h, #12               // V dither high
+
+        mov             w9, w3                          // chrFilterSize counter
+        mov             x10, x2                         // chrFilter pointer
+        mov             x11, x4                         // chrUSrc base
+        mov             x12, x5                         // chrVSrc base
+
+2:
+        ldr             h6, [x10], #2                   // Load filter coefficient
+
+        ldr             x25, [x11], #8                  // chrUSrc[j]
+        ldr             x26, [x12], #8                  // chrVSrc[j]
+        add             x25, x25, x8, lsl #1            // &chrUSrc[j][i]
+        add             x26, x26, x8, lsl #1            // &chrVSrc[j][i]
+        add             x15, x25, #16
+        add             x16, x26, #16
+
+        ld1             {v2.8h}, [x25]                 // U samples 0-7
+        ld1             {v3.8h}, [x26]                 // V samples 0-7
+
+        ld1             {v4.8h}, [x15]                 // U samples 8-15
+        ld1             {v5.8h}, [x16]                 // V samples 8-15
+        subs            w9, w9, #1
+
+        smlal           v8.4s, v2.4h, v6.h[0]
+        smlal2          v9.4s, v2.8h, v6.h[0]
+        smlal           v10.4s, v3.4h, v6.h[0]
+        smlal2          v11.4s, v3.8h, v6.h[0]
+
+        smlal           v12.4s, v4.4h, v6.h[0]
+        smlal2          v13.4s, v4.8h, v6.h[0]
+        smlal           v14.4s, v5.4h, v6.h[0]
+        smlal2          v15.4s, v5.8h, v6.h[0]
+
+        b.gt            2b
+
+        sqshrun         v2.4h, v8.4s, #16             // Process and store first 8 pixels
+        sqshrun2        v2.8h, v9.4s, #16
+        sqshrun         v3.4h, v10.4s, #16
+        sqshrun2        v3.8h, v11.4s, #16
+
+        sqshrun         v4.4h, v12.4s, #16             // Process and store next 8 pixels
+        sqshrun2        v4.8h, v13.4s, #16
+        sqshrun         v5.4h, v14.4s, #16
+        sqshrun2        v5.8h, v15.4s, #16
+
+        cbz             w0, 3f
+
+        uqshrn          v8.8b, v2.8h, #3              // Storing U
+        uqshrn2         v8.16b, v4.8h, #3
+        uqshrn          v9.8b, v3.8h, #3              // Storing V
+        uqshrn2         v9.16b, v5.8h, #3
+
+        st2             {v8.16b, v9.16b}, [x6], #32
+        b               4f
+3:
+        uqshrn          v8.8b, v3.8h, #3              // Storing V
+        uqshrn2         v8.16b, v5.8h, #3
+        uqshrn          v9.8b, v2.8h, #3              // Storing U
+        uqshrn2         v9.16b, v4.8h, #3
+
+        st2             {v8.16b, v9.16b}, [x6], #32
+4:
+        subs            w7, w7, #16
+        add             x8, x8, #16
+        b.gt            1b
+
+5:
+        cmp             w7, #8
+        blt             10f
+6:
+        ushll           v8.4s, v0.4h, #12               // U dither low
+        ushll2          v9.4s, v0.8h, #12               // U dither high
+        ushll           v10.4s, v1.4h, #12               // V dither low
+        ushll2          v11.4s, v1.8h, #12               // V dither high
+
+        mov             w9, w3                          // chrFilterSize counter
+        mov             x10, x2                         // chrFilter pointer
+        mov             x11, x4                         // chrUSrc base
+        mov             x12, x5                         // chrVSrc base
+
+7:
+        ldr             h6, [x10], #2                   // Load filter coefficient
+
+        ldr             x25, [x11], #8                  // chrUSrc[j]
+        ldr             x26, [x12], #8                  // chrVSrc[j]
+        add             x25, x25, x8, lsl #1            // &chrUSrc[j][i]
+        add             x26, x26, x8, lsl #1            // &chrVSrc[j][i]
+
+        ld1             {v12.8h}, [x25]                 // U samples
+        ld1             {v13.8h}, [x26]                 // V samples
+        subs            w9, w9, #1
+
+        smlal           v8.4s, v12.4h, v6.h[0]
+        smlal2          v9.4s, v12.8h, v6.h[0]
+        smlal           v10.4s, v13.4h, v6.h[0]
+        smlal2          v11.4s, v13.8h, v6.h[0]
+
+        b.gt            7b
+
+        sqshrun         v4.4h, v8.4s, #16             // Final processing and store
+        sqshrun2        v4.8h, v9.4s, #16
+        sqshrun         v5.4h, v10.4s, #16
+        sqshrun2        v5.8h, v11.4s, #16
+
+        cbz             w0, 8f
+        uqshrn          v8.8b, v4.8h, #3              // Storing U
+        uqshrn          v9.8b, v5.8h, #3              // Storing V
+        st2             {v8.8b, v9.8b}, [x6], #16
+        b               9f
+8:
+        uqshrn          v8.8b, v5.8h, #3              // Storing V
+        uqshrn          v9.8b, v4.8h, #3              // Storing U
+        st2             {v8.8b, v9.8b}, [x6], #16
+9:
+        subs            w7, w7, #8
+        add             x8, x8, #8
+
+10:
+        cbz             w7, 15f                         // Scalar loop
+
+11:
+        and             x15, x8, #7
+        ldrb            w9, [x1, x15]
+        sxtw            x9, w9
+        lsl             x9, x9, #12                     // u = chrDither[i & 7] << 12;
+
+        add             x15, x8, #3
+        and             x15, x15, #7
+        ldrb            w10, [x1, x15]
+        sxtw            x10, w10
+        lsl             x10, x10, #12                   // v = chrDither[(i + 3) & 7] << 12;
+
+        mov             w11, w3                         // chrFilterSize counter
+        mov             x12, x2                         // chrFilter pointer
+        mov             x25, x4                         // chrUSrc base
+        mov             x26, x5                         // chrVSrc base
+
+12:
+        ldrsh           x16, [x12], #2
+
+        ldr             x17, [x25], #8                  // chrUSrc[j]
+        ldr             x19, [x26], #8                  // chrVSrc[j]
+        add             x17, x17, x8, lsl #1            // &chrUSrc[j][i]
+        add             x19, x19, x8, lsl #1            // &chrVSrc[j][i]
+
+        ldrsh           x20, [x17]
+        ldrsh           x21, [x19]
+
+        madd            x9, x16, x20, x9
+        madd            x10, x16, x21, x10
+
+        subs            w11, w11, #1
+        b.gt            12b
+
+        asr             x9, x9, #19                     // Process and store U and V
+        asr             x10, x10, #19
+
+        cmp             x9, #0
+        csel            x9, x9, xzr, ge
+        cmp             x10, #0
+        csel            x10, x10, xzr, ge
+
+        mov             x22, #1
+        lsl             x22, x22, #8
+        sub             x22, x22, #1
+
+        cmp             x9, x22
+        csel            x9, x22, x9, gt
+        cmp             x10, x22
+        csel            x10, x22, x10, gt
+
+        cbz             w0, 13f
+        strb            w9, [x6], #1                    // Storing U
+        strb            w10, [x6], #1                   // Storing V
+        b               14f
+13:
+        strb            w10, [x6], #1                   // Storing V
+        strb            w9, [x6], #1                    // Storing U
+
+14:
+        subs            w7, w7, #1
+        add             x8, x8, #1
+        b.gt            11b
+15:
+        ldp             x21, x22, [sp, #0x10]
+        ldp             x25, x26, [sp, #0x20]
+        ldp             d8, d9, [sp, #0x30]
+        ldp             d10, d11, [sp, #0x40]
+        ldp             d12, d13, [sp, #0x50]
+        ldp             d14, d15, [sp, #0x60]
+        ldp             x19, x20, [sp], #0x70
+        ret
+endfunc
diff --git a/libs/ffmpeg/libswscale/arm64ec/range_convert_neon.S b/libs/ffmpeg/libswscale/arm64ec/range_convert_neon.S
new file mode 100644
index 000000000000..56869de4426b
--- /dev/null
+++ b/libs/ffmpeg/libswscale/arm64ec/range_convert_neon.S
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2024 Ramiro Polla
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/aarch64/asm.S"
+
+.macro lumConvertRange fromto, bit_depth
+function ff_lumRange\fromto\()Jpeg\bit_depth\()_neon, export=1
+// x0  int16_t *dst
+// w1  int width
+// w2  uint32_t coeff
+// x3  int64_t offset
+        stp             d8, d9, [sp, #-64]!
+        stp             d10, d11, [sp, #16]
+        stp             d12, d13, [sp, #32]
+        stp             d14, d15, [sp, #48]
+.if \bit_depth == 16
+.ifc \fromto, To
+        movi            v5.4s, #1
+        movi            v4.4s, #1<<3, lsl #16
+        sub             v4.4s, v4.4s, v5.4s
+.endif
+        dup             v5.4s, w2
+        dup             v6.2d, x3
+1:
+        ld1             {v0.4s, v1.4s}, [x0]
+        mov             v8.16b, v6.16b
+        mov             v9.16b, v6.16b
+        mov             v10.16b, v6.16b
+        mov             v11.16b, v6.16b
+        smlal           v8.2d, v0.2s, v5.2s
+        smlal2          v9.2d, v0.4s, v5.4s
+        smlal           v10.2d, v1.2s, v5.2s
+        smlal2          v11.2d, v1.4s, v5.4s
+        shrn            v0.2s, v8.2d, 18
+        shrn2           v0.4s, v9.2d, 18
+        shrn            v1.2s, v10.2d, 18
+        shrn2           v1.4s, v11.2d, 18
+        subs            w1, w1, #8
+.ifc \fromto, To
+        smin            v0.4s, v0.4s, v4.4s
+        smin            v1.4s, v1.4s, v4.4s
+.endif
+        st1             {v0.4s, v1.4s}, [x0], #32
+        b.gt            1b
+.else
+        dup             v5.4s, w2
+        dup             v6.4s, w3
+1:
+        ld1             {v0.8h}, [x0]
+        mov             v8.16b, v6.16b
+        mov             v10.16b, v6.16b
+        sxtl            v12.4s, v0.4h
+        sxtl2           v14.4s, v0.8h
+        mla             v8.4s, v12.4s, v5.4s
+        mla             v10.4s, v14.4s, v5.4s
+.ifc \fromto, To
+        sqshrn          v0.4h, v8.4s, 14
+        sqshrn2         v0.8h, v10.4s, 14
+.else
+        shrn            v0.4h, v8.4s, 14
+        shrn2           v0.8h, v10.4s, 14
+.endif
+        subs            w1, w1, #8
+        st1             {v0.8h}, [x0], #16
+        b.gt            1b
+.endif
+        ldp             d10, d11, [sp, #16]
+        ldp             d12, d13, [sp, #32]
+        ldp             d14, d15, [sp, #48]
+        ldp             d8, d9, [sp], #64
+        ret
+endfunc
+.endm
+
+.macro chrConvertRange fromto, bit_depth
+function ff_chrRange\fromto\()Jpeg\bit_depth\()_neon, export=1
+// x0  int16_t *dstU
+// x1  int16_t *dstV
+// w2  int width
+// w3  uint32_t coeff
+// x4  int64_t offset
+        stp             d8, d9, [sp, #-64]!
+        stp             d10, d11, [sp, #16]
+        stp             d12, d13, [sp, #32]
+        stp             d14, d15, [sp, #48]
+.if \bit_depth == 16
+.ifc \fromto, To
+        movi            v5.4s, #1
+        movi            v4.4s, #1<<3, lsl #16
+        sub             v4.4s, v4.4s, v5.4s
+.endif
+        dup             v5.4s, w3
+        dup             v6.2d, x4
+1:
+        ld1             {v0.4s, v1.4s}, [x0]
+        ld1             {v2.4s, v3.4s}, [x1]
+        mov             v8.16b, v6.16b
+        mov             v9.16b, v6.16b
+        mov             v10.16b, v6.16b
+        mov             v11.16b, v6.16b
+        mov             v12.16b, v6.16b
+        mov             v13.16b, v6.16b
+        mov             v14.16b, v6.16b
+        mov             v15.16b, v6.16b
+        smlal           v8.2d, v0.2s, v5.2s
+        smlal2          v9.2d, v0.4s, v5.4s
+        smlal           v10.2d, v1.2s, v5.2s
+        smlal2          v11.2d, v1.4s, v5.4s
+        smlal           v12.2d, v2.2s, v5.2s
+        smlal2          v13.2d, v2.4s, v5.4s
+        smlal           v14.2d, v3.2s, v5.2s
+        smlal2          v15.2d, v3.4s, v5.4s
+        shrn            v0.2s, v8.2d, 18
+        shrn2           v0.4s, v9.2d, 18
+        shrn            v1.2s, v10.2d, 18
+        shrn2           v1.4s, v11.2d, 18
+        shrn            v2.2s, v12.2d, 18
+        shrn2           v2.4s, v13.2d, 18
+        shrn            v3.2s, v14.2d, 18
+        shrn2           v3.4s, v15.2d, 18
+        subs            w2, w2, #8
+.ifc \fromto, To
+        smin            v0.4s, v0.4s, v4.4s
+        smin            v1.4s, v1.4s, v4.4s
+        smin            v2.4s, v2.4s, v4.4s
+        smin            v3.4s, v3.4s, v4.4s
+.endif
+        st1             {v0.4s, v1.4s}, [x0], #32
+        st1             {v2.4s, v3.4s}, [x1], #32
+        b.gt            1b
+.else
+        dup             v5.4s, w3
+        dup             v6.4s, w4
+1:
+        ld1             {v0.8h}, [x0]
+        ld1             {v1.8h}, [x1]
+        mov             v8.16b, v6.16b
+        mov             v9.16b, v6.16b
+        mov             v10.16b, v6.16b
+        mov             v11.16b, v6.16b
+        sxtl            v12.4s, v0.4h
+        sxtl            v13.4s, v1.4h
+        sxtl2           v14.4s, v0.8h
+        sxtl2           v15.4s, v1.8h
+        mla             v8.4s, v12.4s, v5.4s
+        mla             v9.4s, v13.4s, v5.4s
+        mla             v10.4s, v14.4s, v5.4s
+        mla             v11.4s, v15.4s, v5.4s
+.ifc \fromto, To
+        sqshrn          v0.4h, v8.4s, 14
+        sqshrn          v1.4h, v9.4s, 14
+        sqshrn2         v0.8h, v10.4s, 14
+        sqshrn2         v1.8h, v11.4s, 14
+.else
+        shrn            v0.4h, v8.4s, 14
+        shrn            v1.4h, v9.4s, 14
+        shrn2           v0.8h, v10.4s, 14
+        shrn2           v1.8h, v11.4s, 14
+.endif
+        subs            w2, w2, #8
+        st1             {v0.8h}, [x0], #16
+        st1             {v1.8h}, [x1], #16
+        b.gt            1b
+.endif
+        ldp             d10, d11, [sp, #16]
+        ldp             d12, d13, [sp, #32]
+        ldp             d14, d15, [sp, #48]
+        ldp             d8, d9, [sp], #64
+        ret
+endfunc
+.endm
+
+lumConvertRange To,    8
+lumConvertRange To,   16
+chrConvertRange To,    8
+chrConvertRange To,   16
+lumConvertRange From,  8
+lumConvertRange From, 16
+chrConvertRange From,  8
+chrConvertRange From, 16
diff --git a/libs/ffmpeg/libswscale/arm64ec/rgb2rgb.c b/libs/ffmpeg/libswscale/arm64ec/rgb2rgb.c
new file mode 100644
index 000000000000..fda92137350d
--- /dev/null
+++ b/libs/ffmpeg/libswscale/arm64ec/rgb2rgb.c
@@ -0,0 +1 @@
+#include "../aarch64/rgb2rgb.c"
diff --git a/libs/ffmpeg/libswscale/arm64ec/rgb2rgb_neon.S b/libs/ffmpeg/libswscale/arm64ec/rgb2rgb_neon.S
new file mode 100644
index 000000000000..e17979b93e2d
--- /dev/null
+++ b/libs/ffmpeg/libswscale/arm64ec/rgb2rgb_neon.S
@@ -0,0 +1,888 @@
+/*
+ * Copyright (c) 2020 Martin Storsjo
+ * Copyright (c) 2024 Ramiro Polla
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/aarch64/asm.S"
+
+#define RGB2YUV_COEFFS 16*4+16*32
+#define BY v0.h[0]
+#define GY v0.h[1]
+#define RY v0.h[2]
+#define BU v1.h[0]
+#define GU v1.h[1]
+#define RU v1.h[2]
+#define BV v2.h[0]
+#define GV v2.h[1]
+#define RV v2.h[2]
+
+const shuf_0321_tbl, align=4
+        .byte  0,  3,  2,  1
+        .byte  4,  7,  6,  5
+        .byte  8,  11, 10, 9
+        .byte  12, 15, 14, 13
+endconst
+
+const shuf_1230_tbl, align=4
+        .byte  1,  2,  3,  0
+        .byte  5,  6,  7,  4
+        .byte  9,  10, 11, 8
+        .byte  13, 14, 15, 12
+endconst
+
+const shuf_2103_tbl, align=4
+        .byte  2,  1,  0,  3
+        .byte  6,  5,  4,  7
+        .byte  10, 9,  8,  11
+        .byte  14, 13, 12, 15
+endconst
+
+const shuf_3012_tbl, align=4
+        .byte  3,  0,  1,  2
+        .byte  7,  4,  5,  6
+        .byte  11, 8,  9,  10
+        .byte  15, 12, 13, 14
+endconst
+
+const shuf_3210_tbl, align=4
+        .byte  3,  2,  1,  0
+        .byte  7,  6,  5,  4
+        .byte  11, 10, 9,  8
+        .byte  15, 14, 13, 12
+endconst
+
+const shuf_3102_tbl, align=4
+        .byte  3,  1,  0,  2
+        .byte  7,  5,  4,  6
+        .byte  11, 9,  8,  10
+        .byte  15, 13, 12, 14
+endconst
+
+const shuf_2013_tbl, align=4
+        .byte  2,  0,  1,  3
+        .byte  6,  4,  5,  7
+        .byte  10, 8,  9,  11
+        .byte  14, 12, 13, 15
+endconst
+
+const shuf_1203_tbl, align=4
+        .byte  1,  2,  0,  3
+        .byte  5,  6,  4,  7
+        .byte  9,  10, 8,  11
+        .byte  13, 14, 12, 15
+endconst
+
+const shuf_2130_tbl, align=4
+        .byte  2,  1,  3,  0
+        .byte  6,  5,  7,  4
+        .byte  10, 9,  11, 8
+        .byte  14, 13, 15, 12
+endconst
+
+// rgb32tobgr24: tbl indices for 2-register sliding window (ldp+tbl+stp approach)
+// Converts 16 BGRA pixels (64 bytes) to 16 BGR pixels (48 bytes) by dropping alpha.
+// Each 16-byte output register selects 3-of-4 bytes from a {Vn, Vn+1} pair.
+const rgb32tobgr24_tbl, align=4
+        // out0 from {v0,v1}: pixels 0-5⅓ → B0 G0 R0 B1 G1 R1 B2 G2 R2 B3 G3 R3 B4 G4 R4 B5
+        .byte  0,  1,  2,  4,  5,  6,  8,  9, 10, 12, 13, 14, 16, 17, 18, 20
+        // out1 from {v1,v2}: pixels 5⅓-10⅔ → G5 R5 B6 G6 R6 B7 G7 R7 B8 G8 R8 B9 G9 R9 B10 G10
+        .byte  5,  6,  8,  9, 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 24, 25
+        // out2 from {v2,v3}: pixels 10⅔-15 → R10 B11 G11 R11 B12 G12 R12 B13 G13 R13 B14 G14 R14 B15 G15 R15
+        .byte 10, 12, 13, 14, 16, 17, 18, 20, 21, 22, 24, 25, 26, 28, 29, 30
+endconst
+
+// rgb24tobgr32: tbl indices for sliding window (ldp+tbl+orr+stp approach)
+// Converts 16 BGR pixels (48 bytes) to 16 BGRA pixels (64 bytes) by inserting alpha=255.
+// Out-of-range index 128 produces 0 from tbl; orr with alpha mask fills in 0xFF.
+const rgb24tobgr32_tbl, align=4
+        // out0 from {v0}: pixels 0-3 → B0 G0 R0 _ B1 G1 R1 _ B2 G2 R2 _ B3 G3 R3 _
+        .byte  0,  1,  2, 128,  3,  4,  5, 128,  6,  7,  8, 128,  9, 10, 11, 128
+        // out1 from {v0,v1}: pixels 4-7 → B4 G4 R4 _ B5 G5 R5 _ B6 G6 R6 _ B7 G7 R7 _
+        .byte 12, 13, 14, 128, 15, 16, 17, 128, 18, 19, 20, 128, 21, 22, 23, 128
+        // out2 from {v1,v2}: pixels 8-11 → B8 G8 R8 _ B9 G9 R9 _ B10 G10 R10 _ B11 G11 R11 _
+        .byte  8,  9, 10, 128, 11, 12, 13, 128, 14, 15, 16, 128, 17, 18, 19, 128
+        // out3 from {v2}: pixels 12-15 → B12 G12 R12 _ B13 G13 R13 _ B14 G14 R14 _ B15 G15 R15 _
+        .byte  4,  5,  6, 128,  7,  8,  9, 128, 10, 11, 12, 128, 13, 14, 15, 128
+endconst
+
+// convert rgb to 16-bit y, u, or v
+// uses v3 and v4
+
+.macro rgbconv16 dst, b, g, r, bc, gc, rc, shr_bits
+        smull           v3.4s, \b\().4h, \bc
+        smlal           v3.4s, \g\().4h, \gc
+        smlal           v3.4s, \r\().4h, \rc
+        smull2          v4.4s, \b\().8h, \bc
+        smlal2          v4.4s, \g\().8h, \gc
+        smlal2          v4.4s, \r\().8h, \rc        // v3:v4 = b * bc + g * gc + r * rc (32-bit)
+        shrn            \dst\().4h, v3.4s, \shr_bits
+        shrn2           \dst\().8h, v4.4s, \shr_bits       // dst = b * bc + g * gc + r * rc (16-bit)
+.endm
+
+// void ff_rgb24toyv12_neon(const uint8_t *src, uint8_t *ydst, uint8_t *udst,
+//                          uint8_t *vdst, int width, int height, int lumStride,
+//                          int chromStride, int srcStride, int32_t *rgb2yuv);
+function ff_rgb24toyv12_neon, export=1
+// x0  const uint8_t *src
+// x1  uint8_t *ydst
+// x2  uint8_t *udst
+// x3  uint8_t *vdst
+// w4  int width
+// w5  int height
+// w6  int lumStride
+// w7  int chromStride
+        ldrsw           x12, [sp]
+        ldr             x15, [sp, #8]
+// x12 int srcStride
+// x15 int32_t *rgb2yuv
+        stp             d8, d9, [sp, #-64]!
+        stp             d10, d11, [sp, #16]
+        stp             d12, d13, [sp, #32]
+        stp             d14, d15, [sp, #48]
+
+        // extend width and stride parameters
+        uxtw            x4, w4
+        sxtw            x6, w6
+        sxtw            x7, w7
+
+        // src1 = x0
+        // src2 = x10
+        add             x10, x0,  x12               // x10 = src + srcStride
+        lsl             x12, x12, #1                // srcStride *= 2
+        add             x11, x4,  x4, lsl #1        // x11 = 3 * width
+        sub             x12, x12, x11               // srcPadding = (2 * srcStride) - (3 * width)
+
+        // ydst1 = x1
+        // ydst2 = x11
+        add             x11, x1,  x6                // x11 = ydst + lumStride
+        lsl             x6,  x6,  #1                // lumStride *= 2
+        sub             x6,  x6,  x4                // lumPadding = (2 * lumStride) - width
+
+        sub             x7,  x7,  x4, lsr #1        // chromPadding = chromStride - (width / 2)
+
+        // load rgb2yuv coefficients into v0, v1, and v2
+        add             x15, x15, #RGB2YUV_COEFFS
+        ld1             {v0.8h-v2.8h}, [x15]        // load 24 values
+
+        // load offset constants
+
+1:
+        mov             w15, w4                     // w15 = width
+
+2:
+        // load first line
+        ld3             {v11.16b, v12.16b, v13.16b}, [x0], #48
+
+        // widen first line to 16-bit
+        uxtl            v8.8h, v11.8b               // v8 = B11
+        uxtl            v9.8h, v12.8b               // v9 = G11
+        uxtl            v10.8h, v13.8b              // v10 = R11
+        uxtl2           v11.8h, v11.16b             // v11 = B12
+        uxtl2           v12.8h, v12.16b             // v12 = G12
+        uxtl2           v13.8h, v13.16b             // v13 = R12
+
+        // calculate Y values for first line
+        rgbconv16       v14, v8, v9, v10, BY, GY, RY, #7 // v24 = Y11
+        rgbconv16       v15, v11, v12, v13, BY, GY, RY, #7 // v25 = Y12
+        movi            v3.8h,  #0x10, lsl #8
+
+        // pairwise add and save rgb values to calculate average
+        addp            v5.8h, v8.8h, v11.8h
+        addp            v6.8h, v9.8h, v12.8h
+        addp            v7.8h, v10.8h, v13.8h
+
+        addhn           v14.8b, v14.8h, v3.8h
+        addhn           v15.8b, v15.8h, v3.8h
+        st1             {v14.8b, v15.8b}, [x1], #16 // store ydst1
+
+        // load second line
+        ld3             {v11.16b, v12.16b, v13.16b}, [x10], #48
+
+        // widen second line to 16-bit
+        uxtl            v8.8h, v11.8b               // v8 = B21
+        uxtl            v9.8h, v12.8b               // v9 = G21
+        uxtl            v10.8h, v13.8b              // v10 = R21
+        uxtl2           v11.8h, v11.16b             // v11 = B22
+        uxtl2           v12.8h, v12.16b             // v12 = G22
+        uxtl2           v13.8h, v13.16b             // v13 = R22
+
+        // calculate Y values for second line
+        rgbconv16       v14, v8, v9, v10, BY, GY, RY, #7 // v26 = Y21
+        rgbconv16       v15, v11, v12, v13, BY, GY, RY, #7 // v27 = Y22
+        movi            v3.8h,  #0x10, lsl #8
+
+        // pairwise add rgb values to calculate average
+        addp            v8.8h, v8.8h, v11.8h
+        addp            v9.8h, v9.8h, v12.8h
+        addp            v10.8h, v10.8h, v13.8h
+
+        addhn           v14.8b, v14.8h, v3.8h
+        addhn           v15.8b, v15.8h, v3.8h
+        st1             {v14.8b, v15.8b}, [x11], #16 // store ydst2
+
+        // calculate sum of r, g, b components in 2x2 blocks
+        add             v8.8h, v8.8h, v5.8h
+        add             v9.8h, v9.8h, v6.8h
+        add             v10.8h, v10.8h, v7.8h
+
+        // calculate U and V values
+        rgbconv16       v14, v8, v9, v10, BU, GU, RU, #9 // v28 = U
+        rgbconv16       v15, v8, v9, v10, BV, GV, RV, #9 // v29 = V
+        movi            v3.8h, #0x80, lsl #8
+        addhn           v14.8b, v14.8h, v3.8h
+        addhn           v15.8b, v15.8h, v3.8h
+
+        subs            w15, w15, #16
+
+        // store output
+        st1             {v14.8b}, [x2], #8          // store udst
+        st1             {v15.8b}, [x3], #8          // store vdst
+
+        b.gt            2b
+
+        subs            w5,  w5,  #2
+
+        // row += 2
+        add             x0,  x0,  x12               // src1  += srcPadding
+        add             x10, x10, x12               // src2  += srcPadding
+        add             x1,  x1,  x6                // ydst1 += lumPadding
+        add             x11, x11, x6                // ydst2 += lumPadding
+        add             x2,  x2,  x7                // udst  += chromPadding
+        add             x3,  x3,  x7                // vdst  += chromPadding
+        b.gt            1b
+
+        ldp             d10, d11, [sp, #16]
+        ldp             d12, d13, [sp, #32]
+        ldp             d14, d15, [sp, #48]
+        ldp             d8, d9, [sp], #64
+        ret
+endfunc
+
+// void ff_rgb24tobgr24_neon(const uint8_t *src, uint8_t *dst, int src_size);
+function ff_rgb24tobgr24_neon, export=1
+        // x0 = src, x1 = dst, w2 = src_size (bytes)
+
+        // Fast path: 48 bytes (16 pixels) per iteration
+        subs            w2, w2, #48
+        b.lt            2f
+1:
+        ld3             {v0.16b, v1.16b, v2.16b}, [x0], #48
+        subs            w2, w2, #48
+        mov             v3.16b, v0.16b
+        mov             v0.16b, v2.16b
+        mov             v2.16b, v3.16b
+        st3             {v0.16b, v1.16b, v2.16b}, [x1], #48
+        b.ge            1b
+2:
+        add             w2, w2, #48
+        // Medium path: 24 bytes (8 pixels)
+        cmp             w2, #24
+        b.lt            3f
+        ld3             {v0.8b, v1.8b, v2.8b}, [x0], #24
+        sub             w2, w2, #24
+        mov             v3.8b, v0.8b
+        mov             v0.8b, v2.8b
+        mov             v2.8b, v3.8b
+        st3             {v0.8b, v1.8b, v2.8b}, [x1], #24
+3:
+        // Scalar tail: 3 bytes (1 pixel) at a time
+        cmp             w2, #3
+        b.lt            4f
+5:
+        ldrb            w4, [x0, #1]
+        ldrb            w5, [x0, #2]
+        ldrb            w3, [x0], #3
+        subs            w2, w2, #3
+        strb            w4, [x1, #1]
+        strb            w3, [x1, #2]
+        strb            w5, [x1], #3
+        b.gt            5b
+4:
+        ret
+endfunc
+
+// void ff_rgb32tobgr24_neon(const uint8_t *src, uint8_t *dst, int src_size);
+function ff_rgb32tobgr24_neon, export=1
+        // x0 = src (BGRA), x1 = dst (BGR), w2 = src_size (bytes)
+        stp             d8, d9, [sp, #-16]!
+
+        // Load 3 tbl permutation masks for 2-register sliding window
+        movrel          x3, rgb32tobgr24_tbl
+        ld1             {v7.16b, v8.16b, v9.16b}, [x3]
+
+        // Fast path: 64 bytes input (16 pixels) → 48 bytes output
+        // Uses ldp+tbl(2-reg sliding window)+stp to avoid expensive ld4/st3.
+        // Post-indexed addressing eliminates pointer-advance instructions.
+        // subs placed between loads and tbl to fill load-latency gap on
+        // in-order cores (A55).
+        subs            w2, w2, #64
+        b.lt            2f
+1:
+        ldp             q0, q1, [x0], #64
+        ldp             q2, q3, [x0, #-32]
+        subs            w2, w2, #64
+        tbl             v4.16b, {v0.16b, v1.16b}, v7.16b
+        tbl             v5.16b, {v1.16b, v2.16b}, v8.16b
+        tbl             v6.16b, {v2.16b, v3.16b}, v9.16b
+        stp             q4, q5, [x1], #48
+        str             q6, [x1, #-16]
+        b.ge            1b
+2:
+        add             w2, w2, #64
+        // Medium path: 32 bytes input (8 pixels) → 24 bytes output
+        cmp             w2, #32
+        b.lt            3f
+        ld4             {v0.8b, v1.8b, v2.8b, v3.8b}, [x0], #32
+        sub             w2, w2, #32
+        st3             {v0.8b, v1.8b, v2.8b}, [x1], #24
+3:
+        // Scalar tail: 4 bytes → 3 bytes at a time
+        // Uses word load + halfword/byte stores to reduce instructions.
+        // On LE: ldr gives A<<24|R<<16|G<<8|B; strh stores [B,G]; lsr+strb stores R.
+        // subs and lsr fill load-use latency. lsr uses a fresh register so
+        // strh and strb can issue independently; add advances x1 off critical path.
+        cmp             w2, #4
+        b.lt            4f
+5:
+        ldr             w3, [x0], #4
+        subs            w2, w2, #4
+        lsr             w4, w3, #16
+        strh            w3, [x1]
+        strb            w4, [x1, #2]
+        add             x1, x1, #3
+        b.gt            5b
+4:
+        ldp             d8, d9, [sp], #16
+        ret
+endfunc
+
+// void ff_rgb24tobgr32_neon(const uint8_t *src, uint8_t *dst, int src_size);
+function ff_rgb24tobgr32_neon, export=1
+        // x0 = src (BGR), x1 = dst (BGRA), w2 = src_size (bytes)
+        stp             d8, d9, [sp, #-48]!
+        stp             d10, d11, [sp, #16]
+        stp             d12, d13, [sp, #32]
+
+        // Load tbl permutation indices and alpha mask for the fast path
+        movrel          x3, rgb24tobgr32_tbl
+        ld1             {v8.16b, v9.16b, v10.16b, v11.16b}, [x3]
+        movi            v12.4s, #255, lsl #24             // Alpha mask: 00 00 00 FF per pixel
+
+        // Fast path: 48 bytes input (16 pixels) → 64 bytes output
+        // Uses ldp+tbl+orr+stp to avoid expensive ld3/st4 structure load/stores.
+        // tbl produces 0 for alpha positions; orr fills in 0xFF.
+        // Post-indexed addressing eliminates pointer-advance instructions.
+        // tbl/orr interleaved so each orr starts as soon as its tbl result
+        // is ready, hiding latency on narrow in-order cores (A55).
+        subs            w2, w2, #48
+        b.lt            2f
+1:
+        ldp             q0, q1, [x0], #48
+        ldr             q2, [x0, #-16]
+        subs            w2, w2, #48
+        tbl             v4.16b, {v0.16b}, v8.16b
+        tbl             v5.16b, {v0.16b, v1.16b}, v9.16b
+        orr             v4.16b, v4.16b, v12.16b
+        tbl             v6.16b, {v1.16b, v2.16b}, v10.16b
+        orr             v5.16b, v5.16b, v12.16b
+        tbl             v7.16b, {v2.16b}, v11.16b
+        orr             v6.16b, v6.16b, v12.16b
+        stp             q4, q5, [x1], #64
+        orr             v7.16b, v7.16b, v12.16b
+        stp             q6, q7, [x1, #-32]
+        b.ge            1b
+2:
+        add             w2, w2, #48
+        // Medium path: 24 bytes input (8 pixels) → 32 bytes output
+        cmp             w2, #24
+        b.lt            3f
+        movi            v3.8b, #255
+        ld3             {v0.8b, v1.8b, v2.8b}, [x0], #24
+        sub             w2, w2, #24
+        st4             {v0.8b, v1.8b, v2.8b, v3.8b}, [x1], #32
+3:
+        // Scalar tail: 3 bytes → 4 bytes at a time
+        // Uses halfword+byte loads, orr to combine with alpha, word store.
+        // On LE: ldrh gives G<<8|B, ldrb gives R; orr assembles 0xFF<<24|R<<16|G<<8|B;
+        // str stores [B,G,R,0xFF]. subs and add placed between loads and first
+        // orr to fill load-use latency on A55.
+        cmp             w2, #3
+        b.lt            4f
+5:
+        ldrh            w4, [x0]
+        ldrb            w5, [x0, #2]
+        add             x0, x0, #3
+        subs            w2, w2, #3
+        orr             w4, w4, w5, lsl #16
+        orr             w4, w4, #0xFF000000
+        str             w4, [x1], #4
+        b.gt            5b
+4:
+        ldp             d10, d11, [sp, #16]
+        ldp             d12, d13, [sp, #32]
+        ldp             d8, d9, [sp], #48
+        ret
+endfunc
+
+// void ff_interleave_bytes_neon(const uint8_t *src1, const uint8_t *src2,
+//                               uint8_t *dest, int width, int height,
+//                               int src1Stride, int src2Stride, int dstStride);
+function ff_interleave_bytes_neon, export=1
+        sub             w5,  w5,  w3
+        sub             w6,  w6,  w3
+        sub             w7,  w7,  w3, lsl #1
+1:
+        ands            w8,  w3,  #0xfffffff0 // & ~15
+        b.eq            3f
+2:
+        ld1             {v0.16b}, [x0], #16
+        ld1             {v1.16b}, [x1], #16
+        subs            w8,  w8,  #16
+        st2             {v0.16b, v1.16b}, [x2], #32
+        b.gt            2b
+
+        tst             w3,  #15
+        b.eq            9f
+
+3:
+        tst             w3,  #8
+        b.eq            4f
+        ld1             {v0.8b}, [x0], #8
+        ld1             {v1.8b}, [x1], #8
+        st2             {v0.8b, v1.8b}, [x2], #16
+4:
+        tst             w3,  #4
+        b.eq            5f
+
+        ld1             {v0.s}[0], [x0], #4
+        ld1             {v1.s}[0], [x1], #4
+        zip1            v0.8b,   v0.8b,   v1.8b
+        st1             {v0.8b}, [x2], #8
+
+5:
+        ands            w8,  w3,  #3
+        b.eq            9f
+6:
+        ldrb            w9,  [x0], #1
+        ldrb            w10, [x1], #1
+        subs            w8,  w8,  #1
+        bfi             w9,  w10, #8,  #8
+        strh            w9,  [x2], #2
+        b.gt            6b
+
+9:
+        subs            w4,  w4,  #1
+        b.eq            0f
+        add             x0,  x0,  w5, sxtw
+        add             x1,  x1,  w6, sxtw
+        add             x2,  x2,  w7, sxtw
+        b               1b
+
+0:
+        ret
+endfunc
+
+// void ff_deinterleave_bytes_neon(const uint8_t *src, uint8_t *dst1, uint8_t *dst2,
+//                                 int width, int height, int srcStride,
+//                                 int dst1Stride, int dst2Stride);
+function ff_deinterleave_bytes_neon, export=1
+        sub             w5,  w5,  w3, lsl #1
+        sub             w6,  w6,  w3
+        sub             w7,  w7,  w3
+1:
+        ands            w8,  w3,  #0xfffffff0 // & ~15
+        b.eq            3f
+2:
+        ld2             {v0.16b, v1.16b}, [x0], #32
+        subs            w8,  w8,  #16
+        st1             {v0.16b}, [x1], #16
+        st1             {v1.16b}, [x2], #16
+        b.gt            2b
+
+        tst             w3,  #15
+        b.eq            9f
+
+3:
+        tst             w3,  #8
+        b.eq            4f
+        ld2             {v0.8b, v1.8b}, [x0], #16
+        st1             {v0.8b}, [x1], #8
+        st1             {v1.8b}, [x2], #8
+4:
+        tst             w3,  #4
+        b.eq            5f
+
+        ld1             {v0.8b}, [x0], #8
+        shrn            v1.8b,   v0.8h, #8
+        xtn             v0.8b,   v0.8h
+        st1             {v0.s}[0], [x1], #4
+        st1             {v1.s}[0], [x2], #4
+
+5:
+        ands            w8,  w3,  #3
+        b.eq            9f
+6:
+        ldrh            w9,  [x0], #2
+        subs            w8,  w8,  #1
+        ubfx            w10, w9,  #8,  #8
+        strb            w9,  [x1], #1
+        strb            w10, [x2], #1
+        b.gt            6b
+
+9:
+        subs            w4,  w4,  #1
+        b.eq            0f
+        add             x0,  x0,  w5, sxtw
+        add             x1,  x1,  w6, sxtw
+        add             x2,  x2,  w7, sxtw
+        b               1b
+
+0:
+        ret
+endfunc
+
+.macro neon_shuf shuf
+function ff_shuffle_bytes_\shuf\()_neon, export=1
+        movrel          x9, shuf_\shuf\()_tbl
+        ld1             {v1.16b}, [x9]
+        and             w5, w2, #~15
+        and             w3, w2, #8
+        and             w4, w2, #4
+        cbz             w5, 2f
+1:
+        ld1             {v0.16b}, [x0], #16
+        subs            w5, w5, #16
+        tbl             v0.16b, {v0.16b}, v1.16b
+        st1             {v0.16b}, [x1], #16
+        b.gt            1b
+2:
+        cbz             w3, 3f
+        ld1             {v0.8b}, [x0], #8
+        tbl             v0.8b, {v0.16b}, v1.8b
+        st1             {v0.8b}, [x1], #8
+3:
+        cbz             w4, 4f
+.if \shuf == 0321
+        ldr             w5, [x0]
+        rev             w5, w5
+        ror             w5, w5, #24
+        str             w5, [x1]
+.endif
+.if \shuf == 1230
+        ldr             w5, [x0]
+        ror             w5, w5, #8
+        str             w5, [x1]
+.endif
+.if \shuf == 2103
+        ldr             w5, [x0]
+        rev             w5, w5
+        ror             w5, w5, #8
+        str             w5, [x1]
+.endif
+.if \shuf == 3012
+        ldr             w5, [x0]
+        ror             w5, w5, #24
+        str             w5, [x1]
+.endif
+.if \shuf == 3210
+        ldr             w5, [x0]
+        rev             w5, w5
+        str             w5, [x1]
+.endif
+.if \shuf == 3102 || \shuf == 2013 || \shuf == 1203 || \shuf == 2130
+        ld1             {v0.s}[0], [x0]
+        tbl             v0.8b, {v0.16b}, v1.8b
+        st1             {v0.s}[0], [x1]
+.endif
+4:
+        ret
+endfunc
+.endm
+
+neon_shuf 0321
+neon_shuf 1230
+neon_shuf 2103
+neon_shuf 3012
+neon_shuf 3102
+neon_shuf 2013
+neon_shuf 1203
+neon_shuf 2130
+neon_shuf 3210
+
+/*
+v0-v7 - two consecutive lines
+x0 - upper Y destination
+x1 - U destination
+x2 - V destination
+x3 - upper src line
+w5 - width/iteration counter - count of line pairs for yuv420, of single lines for 422
+x6 - lum padding
+x7 - chrom padding
+x8 - src padding
+w9 - number of bytes remaining in the tail
+x10 - lower Y destination
+w12 - tmp
+x15 - lower src line
+w16 - tmp
+w17 - set to 1 if last line has to be handled separately (odd height)
+*/
+
+// one fast path iteration processes 16 uyvy tuples
+// is_line_tail is set to 1 when final 16 tuples are being processed
+// skip_storing_chroma is set to 1 when final line is processed and the height is odd
+.macro fastpath_iteration src_fmt, dst_fmt, is_line_tail, skip_storing_chroma
+        ld4             {v0.16b - v3.16b}, [x3], #64
+.if ! \is_line_tail
+        subs            w16, w16, #32
+.endif
+
+.if ! \skip_storing_chroma
+.ifc \dst_fmt, yuv420
+        ld4             {v4.16b - v7.16b}, [x15], #64
+.endif
+
+.ifc \dst_fmt, yuv420                                    // store UV
+.ifc \src_fmt, uyvy
+        uhadd           v0.16b, v4.16b, v0.16b            // halving sum of U
+        uhadd           v2.16b, v6.16b, v2.16b            // halving sum of V
+.else
+        uhadd           v1.16b, v5.16b, v1.16b            // halving sum of U
+        uhadd           v3.16b, v7.16b, v3.16b            // halving sum of V
+.endif
+.endif
+
+.ifc \src_fmt, uyvy
+        st1             {v2.16b}, [x2], #16
+        st1             {v0.16b}, [x1], #16
+.else
+        st1             {v3.16b}, [x2], #16
+        st1             {v1.16b}, [x1], #16
+.endif
+
+.ifc \dst_fmt, yuv420                                    // store_y
+.ifc \src_fmt, uyvy
+        mov             v6.16b, v5.16b
+        st2             {v6.16b,v7.16b}, [x10], #32
+.else
+        mov             v5.16b, v4.16b
+        st2             {v5.16b,v6.16b}, [x10], #32
+.endif
+.endif
+
+.endif // ! \skip_storing_chroma
+
+.ifc \src_fmt, uyvy
+        mov             v2.16b, v1.16b
+        st2             {v2.16b,v3.16b}, [x0], #32
+.else
+        mov             v1.16b, v0.16b
+        st2             {v1.16b,v2.16b}, [x0], #32
+.endif
+.endm
+
+// shift pointers back to width - 32 to process the tail of the line
+// if the height is odd, processing the final line is simplified
+.macro fastpath_shift_back_pointers src_fmt, dst_fmt, is_final_odd_line
+        add             x3, x3, w9, sxtw #1
+        sub             x3, x3, #64
+.if ! \is_final_odd_line
+.ifc \dst_fmt, yuv420
+        add             x15, x15, w9, sxtw #1
+        sub             x15, x15, #64
+        add             x10, x10, w9, sxtw
+        sub             x10, x10, #32
+.endif
+.endif
+        add             x0, x0, w9, sxtw
+        sub             x0, x0, #32
+.if ! \is_final_odd_line
+        asr             w16, w9, #1
+        add             x1, x1, w16, sxtw
+        sub             x1, x1, #16
+        add             x2, x2, w16, sxtw
+        sub             x2, x2, #16
+.endif
+.endm
+
+.macro slowpath_iteration src_fmt, dst_fmt, skip_storing_chroma
+.ifc \dst_fmt, yuv422
+.ifc \src_fmt, uyvy
+        ldrb            w12, [x3], #1
+        ldrb            w16, [x3], #1
+        strb            w12, [x1], #1
+        strb            w16, [x0], #1
+        ldrb            w12, [x3], #1
+        ldrb            w16, [x3], #1
+        strb            w12, [x2], #1
+        strb            w16, [x0], #1
+.else
+        ldrb            w12, [x3], #1
+        ldrb            w16, [x3], #1
+        strb            w12, [x0], #1
+        strb            w16, [x1], #1
+        ldrb            w12, [x3], #1
+        ldrb            w16, [x3], #1
+        strb            w12, [x0], #1
+        strb            w16, [x2], #1
+.endif
+.endif
+.ifc \dst_fmt, yuv420
+.ifc \src_fmt, uyvy
+.if \skip_storing_chroma
+        ldrb            w12, [x3], #2
+        ldrb            w16, [x3], #2
+        strb            w12, [x0], #1
+        strb            w16, [x0], #1
+.else
+        ldrb            w12, [x3], #1
+        ldrb            w16, [x15], #1
+        add             w12, w12, w16
+        lsr             w12, w12, #1
+        strb            w12, [x1], #1
+        ldrb            w16, [x3], #1
+        ldrb            w12, [x15], #1
+        strb            w16, [x0], #1
+        strb            w12, [x10], #1
+        ldrb            w16, [x15], #1
+        ldrb            w12, [x3], #1
+        add             w12, w12, w16
+        lsr             w12, w12, #1
+        strb            w12, [x2], #1
+        ldrb            w16, [x3], #1
+        ldrb            w12, [x15], #1
+        strb            w16, [x0], #1
+        strb            w12, [x10], #1
+.endif
+.else
+.if \skip_storing_chroma
+        ldrb            w12, [x3], #2
+        ldrb            w16, [x3], #2
+        strb            w12, [x0], #1
+        strb            w16, [x0], #1
+.else
+        ldrb            w12, [x3], #1
+        ldrb            w16, [x15], #1
+        strb            w12, [x0], #1
+        strb            w16, [x10], #1
+        ldrb            w12, [x3], #1
+        ldrb            w16, [x15], #1
+        add             w12, w12, w16
+        lsr             w12, w12, #1
+        strb            w12, [x1], #1
+        ldrb            w16, [x3], #1
+        ldrb            w12, [x15], #1
+        strb            w16, [x0], #1
+        strb            w12, [x10], #1
+        ldrb            w16, [x15], #1
+        ldrb            w12, [x3], #1
+        add             w12, w12, w16
+        lsr             w12, w12, #1
+        strb            w12, [x2], #1
+.endif
+.endif
+.endif
+.endm
+
+.macro move_pointers_to_next_line src_fmt, dst_fmt, is_final_odd_line
+        add             x3, x3, x8
+        add             x0, x0, x6
+.ifc \dst_fmt, yuv420
+        add             x15, x15, x8
+        add             x10, x10, x6
+.endif
+        add             x1, x1, x7
+        add             x2, x2, x7
+.endm
+
+.macro interleaved_yuv_to_planar src_fmt, dst_fmt
+function ff_\src_fmt\()to\dst_fmt\()_neon, export=1
+        sxtw            x6, w6
+        sxtw            x7, w7
+        ldrsw           x8, [sp]
+        ands            w11, w4, #~31                     // choose between fast and slow path
+
+.ifc \dst_fmt, yuv420
+        add             x10, x0, x6
+        add             x15, x3, x8
+        add             x8, x8, x8
+        add             x6, x6, x6
+        and             w17, w5, #1
+        asr             w5, w5, #1
+.endif
+        asr             w9, w4, #1
+        sub             x8, x8, w4, sxtw #1               // src offset
+        sub             x6, x6, w4, sxtw                  // lum offset
+        sub             x7, x7, x9                        // chr offset
+
+        b.eq            6f
+
+1:                                                        // fast path - the width is at least 32
+        and             w16, w4, #~31                     // w16 is the main loop counter
+        and             w9, w4, #31                       // w9 holds the remaining width, 0 to 31
+2:
+        fastpath_iteration \src_fmt, \dst_fmt, 0, 0
+        b.ne            2b
+        fastpath_shift_back_pointers \src_fmt, \dst_fmt, 0
+        fastpath_iteration \src_fmt, \dst_fmt, 0, 0
+        subs            w5, w5, #1
+        move_pointers_to_next_line \src_fmt, \dst_fmt
+        b.ne            1b
+
+.ifc \dst_fmt, yuv420                                    // handle the last line in case the height is odd
+        cbz             w17, 3f
+        and             w16, w4, #~31
+4:
+        fastpath_iteration \src_fmt, \dst_fmt, 0, 1
+        b.ne            4b
+        fastpath_shift_back_pointers \src_fmt, \dst_fmt, 1
+        fastpath_iteration \src_fmt, \dst_fmt, 1, 1
+3:
+.endif
+        ret
+
+6:                                                        // slow path - width is at most 31
+        and             w9, w4, #31
+7:
+        subs            w9, w9, #2
+        slowpath_iteration \src_fmt, \dst_fmt, 0
+        b.ne            7b
+        subs            w5, w5, #1
+        move_pointers_to_next_line \src_fmt, \dst_fmt
+        b.ne            6b
+
+.ifc \dst_fmt, yuv420
+        cbz             w17, 8f
+        and             w9, w4, #31
+.ifc \src_fmt, uyvy
+        add             x3, x3, #1
+.endif
+5:
+        subs            w9, w9, #2
+        slowpath_iteration \src_fmt, \dst_fmt, 1
+        b.ne            5b
+8:
+.endif
+        ret
+endfunc
+.endm
+
+interleaved_yuv_to_planar uyvy, yuv422
+interleaved_yuv_to_planar uyvy, yuv420
+interleaved_yuv_to_planar yuyv, yuv422
+interleaved_yuv_to_planar yuyv, yuv420
diff --git a/libs/ffmpeg/libswscale/arm64ec/swscale.c b/libs/ffmpeg/libswscale/arm64ec/swscale.c
new file mode 100644
index 000000000000..3be5505a33d9
--- /dev/null
+++ b/libs/ffmpeg/libswscale/arm64ec/swscale.c
@@ -0,0 +1 @@
+#include "../aarch64/swscale.c"
diff --git a/libs/ffmpeg/libswscale/arm64ec/swscale_unscaled.c b/libs/ffmpeg/libswscale/arm64ec/swscale_unscaled.c
new file mode 100644
index 000000000000..5fc7756d9497
--- /dev/null
+++ b/libs/ffmpeg/libswscale/arm64ec/swscale_unscaled.c
@@ -0,0 +1 @@
+#include "../aarch64/swscale_unscaled.c"
diff --git a/libs/ffmpeg/libswscale/arm64ec/swscale_unscaled_neon.S b/libs/ffmpeg/libswscale/arm64ec/swscale_unscaled_neon.S
new file mode 100644
index 000000000000..8fd76b26d8ed
--- /dev/null
+++ b/libs/ffmpeg/libswscale/arm64ec/swscale_unscaled_neon.S
@@ -0,0 +1 @@
+#include "../aarch64/swscale_unscaled_neon.S"
diff --git a/libs/ffmpeg/libswscale/arm64ec/yuv2rgb_neon.S b/libs/ffmpeg/libswscale/arm64ec/yuv2rgb_neon.S
new file mode 100644
index 000000000000..07925593b975
--- /dev/null
+++ b/libs/ffmpeg/libswscale/arm64ec/yuv2rgb_neon.S
@@ -0,0 +1,359 @@
+/*
+ * Copyright (c) 2016 Matthieu Bouron <matthieu.bouron stupeflix.com>
+ * Copyright (c) 2016 Clément Bœsch <clement stupeflix.com>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "libavutil/aarch64/asm.S"
+
+#define ARGS_OFFSET 0x50
+
+.macro load_yoff_ycoeff yoff ycoeff
+        ldr             w9,  [sp, #\yoff + ARGS_OFFSET]
+        ldr             w10, [sp, #\ycoeff + ARGS_OFFSET]
+.endm
+
+.macro load_dst1_dst2 dst1 linesize1 dst2 linesize2
+        ldr             x10, [sp, #\dst1      + ARGS_OFFSET]
+        ldr             w12, [sp, #\linesize1 + ARGS_OFFSET]
+        ldr             x15, [sp, #\dst2      + ARGS_OFFSET]
+        ldr             w16, [sp, #\linesize2 + ARGS_OFFSET]
+        sub             w12, w12, w0                                    // w12 = linesize1 - width     (padding1)
+        sub             w16, w16, w0                                    // w16 = linesize2 - width     (padding2)
+.endm
+
+.macro load_args_nv12 ofmt
+        ldr             x8,  [sp, #ARGS_OFFSET]                          // table
+        load_yoff_ycoeff 8, 16                                           // y_offset, y_coeff
+        ld1             {v1.1d}, [x8]
+        dup             v0.8h, w10
+        dup             v3.8h, w9
+.ifc \ofmt,gbrp
+        load_dst1_dst2  24, 32, 40, 48
+        sub             w3, w3, w0                                      // w3 = linesize  - width     (padding)
+.else
+ .ifc \ofmt,rgb24
+        add             w17, w0, w0, lsl #1
+        sub             w3, w3, w17                                     // w3 = linesize  - width * 3 (padding)
+ .else
+  .ifc \ofmt,bgr24
+        add             w17, w0, w0, lsl #1
+        sub             w3, w3, w17                                     // w3 = linesize  - width * 3 (padding)
+  .else
+        sub             w3, w3, w0, lsl #2                              // w3 = linesize  - width * 4 (padding)
+  .endif
+ .endif
+.endif
+        sub             w5, w5, w0                                      // w5 = linesizeY - width     (paddingY)
+        sub             w7, w7, w0                                      // w7 = linesizeC - width     (paddingC)
+        neg             w11, w0
+.endm
+
+.macro load_args_nv21 ofmt
+        load_args_nv12  \ofmt
+.endm
+
+.macro load_args_yuv420p ofmt
+        ldr             x19, [sp, #0 + ARGS_OFFSET]                     // srcV
+        ldr             w20, [sp, #8 + ARGS_OFFSET]                     // linesizeV
+        ldr             x8,  [sp, #16 + ARGS_OFFSET]                    // table
+        load_yoff_ycoeff 24, 32                                          // y_offset, y_coeff
+        ld1             {v1.1d}, [x8]
+        dup             v0.8h, w10
+        dup             v3.8h, w9
+.ifc \ofmt,gbrp
+        load_dst1_dst2  40, 48, 56, 64
+        sub             w3, w3, w0                                      // w3 = linesize  - width     (padding)
+.else
+ .ifc \ofmt,rgb24
+        add             w17, w0, w0, lsl #1
+        sub             w3, w3, w17                                     // w3 = linesize  - width * 3 (padding)
+ .else
+  .ifc \ofmt,bgr24
+        add             w17, w0, w0, lsl #1
+        sub             w3, w3, w17                                     // w3 = linesize  - width * 3 (padding)
+  .else
+        sub             w3, w3, w0, lsl #2                              // w3 = linesize  - width * 4 (padding)
+  .endif
+ .endif
+.endif
+        sub             w5, w5, w0                                      // w5 = linesizeY - width     (paddingY)
+        sub             w7,  w7,  w0, lsr #1                            // w7  = linesizeU - width / 2 (paddingU)
+        sub             w20, w20, w0, lsr #1                            // w20 = linesizeV - width / 2 (paddingV)
+        lsr             w11, w0, #1
+        neg             w11, w11
+.endm
+
+.macro load_args_yuva420p ofmt
+        load_args_yuv420p \ofmt
+#if defined(__APPLE__)
+        ldr             x15, [sp, #32 + ARGS_OFFSET]                    // srcA
+        ldr             w16, [sp, #40 + ARGS_OFFSET]                    // linesizeA
+#else
+        ldr             x15, [sp, #40 + ARGS_OFFSET]                    // srcA
+        ldr             w16, [sp, #48 + ARGS_OFFSET]                    // linesizeA
+#endif
+        sub             w16, w16, w0                                    // w16 = linesizeA - width    (paddingA)
+.endm
+
+.macro load_args_yuv422p ofmt
+        ldr             x19, [sp, #0 + ARGS_OFFSET]                     // srcV
+        ldr             w20, [sp, #8 + ARGS_OFFSET]                     // linesizeV
+        ldr             x8,  [sp, #16 + ARGS_OFFSET]                    // table
+        load_yoff_ycoeff 24, 32                                          // y_offset, y_coeff
+        ld1             {v1.1d}, [x8]
+        dup             v0.8h, w10
+        dup             v3.8h, w9
+.ifc \ofmt,gbrp
+        load_dst1_dst2  40, 48, 56, 64
+        sub             w3, w3, w0                                      // w3 = linesize  - width     (padding)
+.else
+ .ifc \ofmt,rgb24
+        add             w17, w0, w0, lsl #1
+        sub             w3, w3, w17                                     // w3 = linesize  - width * 3 (padding)
+ .else
+  .ifc \ofmt,bgr24
+        add             w17, w0, w0, lsl #1
+        sub             w3, w3, w17                                     // w3 = linesize  - width * 3 (padding)
+  .else
+        sub             w3, w3, w0, lsl #2                              // w3 = linesize  - width * 4 (padding)
+  .endif
+ .endif
+.endif
+        sub             w5, w5, w0                                      // w5 = linesizeY - width     (paddingY)
+        sub             w7,  w7,  w0, lsr #1                            // w7  = linesizeU - width / 2 (paddingU)
+        sub             w20, w20, w0, lsr #1                            // w20 = linesizeV - width / 2 (paddingV)
+.endm
+
+.macro load_chroma_nv12
+        ld2             {v8.8b, v9.8b}, [x6], #16
+        ushll           v10.8h, v8.8b, #3
+        ushll           v11.8h, v9.8b, #3
+.endm
+
+.macro load_chroma_nv21
+        ld2             {v8.8b, v9.8b}, [x6], #16
+        ushll           v11.8h, v8.8b, #3
+        ushll           v10.8h, v9.8b, #3
+.endm
+
+.macro load_chroma_yuv420p
+        ld1             {v8.8b}, [ x6], #8
+        ld1             {v9.8b}, [x19], #8
+        ushll           v10.8h, v8.8b, #3
+        ushll           v11.8h, v9.8b, #3
+.endm
+
+.macro load_chroma_yuva420p
+        load_chroma_yuv420p
+.endm
+
+.macro load_chroma_yuv422p
+        load_chroma_yuv420p
+.endm
+
+.macro increment_nv12
+        ands            w17, w1, #1
+        csel            w17, w7, w11, ne                                // incC = (h & 1) ? paddincC : -width
+        add             x6,  x6, w17, sxtw                              // srcC += incC
+.endm
+
+.macro increment_nv21
+        increment_nv12
+.endm
+
+.macro increment_yuv420p
+        ands            w17, w1, #1
+        csel            w17,  w7, w11, ne                               // incU = (h & 1) ? paddincU : -width/2
+        add             x6,  x6,  w17, sxtw                             // srcU += incU
+        csel            w17, w20, w11, ne                               // incV = (h & 1) ? paddincV : -width/2
+        add             x19, x19, w17, sxtw                             // srcV += incV
+.endm
+
+.macro increment_yuva420p
+        increment_yuv420p
+        add             x15, x15, w16, sxtw                             // srcA += paddingA (every row)
+.endm
+
+.macro increment_yuv422p
+        add             x6,  x6,  w7, sxtw                              // srcU += incU
+        add             x19, x19, w20, sxtw                             // srcV += incV
+.endm
+
+.macro compute_rgb r1 g1 b1 r2 g2 b2
+        add             v12.8h, v4.8h, v12.8h                           // Y1 + R1
+        add             v13.8h, v5.8h, v13.8h                           // Y2 + R2
+        add             v14.8h, v4.8h, v14.8h                           // Y1 + G1
+        add             v15.8h, v5.8h, v15.8h                           // Y2 + G2
+        add             v10.8h, v4.8h, v10.8h                           // Y1 + B1
+        add             v11.8h, v5.8h, v11.8h                           // Y2 + B2
+        sqrshrun        \r1, v12.8h, #1                                 // clip_u8((Y1 + R1) >> 1)
+        sqrshrun        \r2, v13.8h, #1                                 // clip_u8((Y2 + R1) >> 1)
+        sqrshrun        \g1, v14.8h, #1                                 // clip_u8((Y1 + G1) >> 1)
+        sqrshrun        \g2, v15.8h, #1                                 // clip_u8((Y2 + G1) >> 1)
+        sqrshrun        \b1, v10.8h, #1                                 // clip_u8((Y1 + B1) >> 1)
+        sqrshrun        \b2, v11.8h, #1                                 // clip_u8((Y2 + B1) >> 1)
+.endm
+
+.macro declare_func ifmt ofmt
+function ff_\ifmt\()_to_\ofmt\()_neon, export=1
+        stp             x19, x20, [sp, #-0x50]!
+        stp             d8, d9, [sp, #0x10]
+        stp             d10, d11, [sp, #0x20]
+        stp             d12, d13, [sp, #0x30]
+        stp             d14, d15, [sp, #0x40]
+        load_args_\ifmt \ofmt
+
+        mov             w9, w1
+1:
+        mov             w8, w0                                          // w8 = width
+2:
+        movi            v2.8h, #4, lsl #8                               // 128 * (1<<3)
+        load_chroma_\ifmt
+        sub             v10.8h, v10.8h, v2.8h                           // U*(1<<3) - 128*(1<<3)
+        sub             v11.8h, v11.8h, v2.8h                           // V*(1<<3) - 128*(1<<3)
+        sqdmulh         v12.8h, v11.8h, v1.h[0]                         // V * v2r            (R)
+        sqdmulh         v14.8h, v10.8h, v1.h[1]                         // U * u2g
+        ld1             {v2.16b}, [x4], #16                             // load luma (interleaved)
+        sqdmulh         v11.8h, v11.8h, v1.h[2]                         //           V * v2g
+        sqdmulh         v10.8h, v10.8h, v1.h[3]                         // U * u2b            (B)
+        ushll           v4.8h, v2.8b,  #3                               // Y1*(1<<3)
+        ushll2          v5.8h, v2.16b, #3                               // Y2*(1<<3)
+# from here use v2
+        add             v14.8h, v14.8h, v11.8h                          // U * u2g + V * v2g  (G)
+# from here use v11
+        sub             v4.8h, v4.8h, v3.8h                             // Y1*(1<<3) - y_offset
+        sub             v5.8h, v5.8h, v3.8h                             // Y2*(1<<3) - y_offset
+        zip2            v13.8h, v12.8h, v12.8h                          // R2
+        zip1            v12.8h, v12.8h, v12.8h                          // R1
+        sqdmulh         v4.8h, v4.8h, v0.8h                             // ((Y1*(1<<3) - y_offset) * y_coeff) >> 15
+        sqdmulh         v5.8h, v5.8h, v0.8h                             // ((Y2*(1<<3) - y_offset) * y_coeff) >> 15
+        zip2            v15.8h, v14.8h, v14.8h                          // G2
+        zip1            v14.8h, v14.8h, v14.8h                          // G1
+        zip2            v11.8h, v10.8h, v10.8h                          // B2
+        zip1            v10.8h, v10.8h, v10.8h                          // B1
+
+.ifc \ofmt,argb // 1 2 3 0
+        compute_rgb     v5.8b,v6.8b,v7.8b, v9.8b,v10.8b,v11.8b
+ .ifc \ifmt,yuva420p
+        ld1             {v4.8b}, [x15], #8
+        ld1             {v8.8b}, [x15], #8
+ .else
+        movi            v4.8b, #255
+        movi            v8.8b, #255
+ .endif
+.endif
+
+.ifc \ofmt,rgba // 0 1 2 3
+        compute_rgb     v4.8b,v5.8b,v6.8b, v8.8b,v9.8b,v10.8b
+ .ifc \ifmt,yuva420p
+        ld1             {v7.8b}, [x15], #8
+        ld1             {v11.8b}, [x15], #8
+ .else
+        movi            v7.8b, #255
+        movi            v11.8b, #255
+ .endif
+.endif
+
+.ifc \ofmt,abgr // 3 2 1 0
+        compute_rgb     v7.8b,v6.8b,v5.8b, v11.8b,v10.8b,v9.8b
+ .ifc \ifmt,yuva420p
+        ld1             {v4.8b}, [x15], #8
+        ld1             {v8.8b}, [x15], #8
+ .else
+        movi            v4.8b, #255
+        movi            v8.8b, #255
+ .endif
+.endif
+
+.ifc \ofmt,bgra // 2 1 0 3
+        compute_rgb     v6.8b,v5.8b,v4.8b, v10.8b,v9.8b,v8.8b
+ .ifc \ifmt,yuva420p
+        ld1             {v7.8b}, [x15], #8
+        ld1             {v11.8b}, [x15], #8
+ .else
+        movi            v7.8b, #255
+        movi            v11.8b, #255
+ .endif
+.endif
+
+.ifc \ofmt,rgb24
+        compute_rgb     v4.8b,v5.8b,v6.8b, v8.8b,v9.8b,v10.8b
+        st3             { v4.8b, v5.8b, v6.8b}, [x2], #24
+        st3             { v8.8b, v9.8b,v10.8b}, [x2], #24
+.else
+ .ifc \ofmt,bgr24
+        compute_rgb     v6.8b,v5.8b,v4.8b, v10.8b,v9.8b,v8.8b
+        st3             { v4.8b, v5.8b, v6.8b}, [x2], #24
+        st3             { v8.8b, v9.8b,v10.8b}, [x2], #24
+ .else
+  .ifc \ofmt,gbrp
+        compute_rgb     v10.8b,v4.8b,v6.8b, v11.8b,v5.8b,v7.8b
+        st1             {  v4.8b,  v5.8b }, [x2],  #16
+        st1             {  v6.8b,  v7.8b }, [x10], #16
+        st1             { v10.8b, v11.8b }, [x15], #16
+  .else
+        st4             { v4.8b, v5.8b, v6.8b, v7.8b}, [x2], #32
+        st4             { v8.8b, v9.8b,v10.8b,v11.8b}, [x2], #32
+  .endif
+ .endif
+.endif
+        subs            w8, w8, #16                                     // width -= 16
+        b.gt            2b
+        add             x2, x2, w3, sxtw                                // dst  += padding
+.ifc \ofmt,gbrp
+        add             x10, x10, w12, sxtw                             // dst1 += padding1
+        add             x15, x15, w16, sxtw                             // dst2 += padding2
+.endif
+        add             x4, x4, w5, sxtw                                // srcY += paddingY
+        increment_\ifmt
+        subs            w1, w1, #1                                      // height -= 1
+        b.gt            1b
+        mov             w0, w9
+        ldp             d8, d9, [sp, #0x10]
+        ldp             d10, d11, [sp, #0x20]
+        ldp             d12, d13, [sp, #0x30]
+        ldp             d14, d15, [sp, #0x40]
+        ldp             x19, x20, [sp], #0x50
+        ret
+endfunc
+.endm
+
+.macro declare_rgb_funcs ifmt
+        declare_func    \ifmt, argb
+        declare_func    \ifmt, rgba
+        declare_func    \ifmt, abgr
+        declare_func    \ifmt, bgra
+        declare_func    \ifmt, gbrp
+        declare_func    \ifmt, rgb24
+        declare_func    \ifmt, bgr24
+.endm
+
+declare_rgb_funcs nv12
+declare_rgb_funcs nv21
+declare_rgb_funcs yuv420p
+declare_rgb_funcs yuv422p
+
+.macro declare_yuva_funcs ifmt
+        declare_func    \ifmt, argb
+        declare_func    \ifmt, rgba
+        declare_func    \ifmt, abgr
+        declare_func    \ifmt, bgra
+.endm
+
+declare_yuva_funcs yuva420p
diff --git a/programs/notepad/main.c b/programs/notepad/main.c
index f4a4503a9599..fef5dd3f1c9a 100644
--- a/programs/notepad/main.c
+++ b/programs/notepad/main.c
@@ -782,9 +782,7 @@ int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
     MSG msg;
     HACCEL hAccel;
     WNDCLASSEXW class;
-    HMONITOR monitor;
-    MONITORINFO info;
-    INT x, y;
+    WINDOWPLACEMENT wp;
 
     InitCommonControls();
 
@@ -810,21 +808,8 @@ int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
     if (!RegisterClassExW(&class)) return FALSE;
 
     /* Setup windows */
-
-    monitor = MonitorFromRect( &main_rect, MONITOR_DEFAULTTOPRIMARY );
-    info.cbSize = sizeof(info);
-    GetMonitorInfoW( monitor, &info );
-
-    x = main_rect.left;
-    y = main_rect.top;
-    if (main_rect.left >= info.rcWork.right ||
-        main_rect.top >= info.rcWork.bottom ||
-        main_rect.right < info.rcWork.left ||
-        main_rect.bottom < info.rcWork.top)
-        x = y = CW_USEDEFAULT;
-
     Globals.hMainWnd =
-        CreateWindowW(L"Notepad", L"Notepad", WS_OVERLAPPEDWINDOW, x, y,
+        CreateWindowW(L"Notepad", L"Notepad", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT,
                       main_rect.right - main_rect.left, main_rect.bottom - main_rect.top,
                       NULL, NULL, Globals.hInstance, NULL);
     if (!Globals.hMainWnd)
@@ -836,7 +821,15 @@ int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE prev, LPSTR cmdline, int show)
     NOTEPAD_InitData();
     DIALOG_FileNew();
 
-    ShowWindow(Globals.hMainWnd, show);
+    wp.length = sizeof(wp);
+    wp.flags = 0;
+    wp.showCmd = show;
+    wp.ptMinPosition.x = -1;
+    wp.ptMinPosition.y = -1;
+    wp.ptMaxPosition.x = -1;
+    wp.ptMaxPosition.y = -1;
+    wp.rcNormalPosition = main_rect;
+    SetWindowPlacement(Globals.hMainWnd, &wp);
     UpdateWindow(Globals.hMainWnd);
     DragAcceptFiles(Globals.hMainWnd, TRUE);
 
diff --git a/programs/taskmgr/taskmgr.c b/programs/taskmgr/taskmgr.c
index ef86e47dc4bc..69b69ef2b541 100644
--- a/programs/taskmgr/taskmgr.c
+++ b/programs/taskmgr/taskmgr.c
@@ -87,6 +87,7 @@ static BOOL OnCreate(HWND hWnd)
     int     nParts[3];
     RECT    rc;
     TCITEMW item;
+    WINDOWPLACEMENT wp;
 
     static WCHAR wszApplications[255];
     static WCHAR wszProcesses[255];
@@ -156,10 +157,20 @@ static BOOL OnCreate(HWND hWnd)
         (TaskManagerSettings.Top != 0) ||
         (TaskManagerSettings.Right != 0) ||
         (TaskManagerSettings.Bottom != 0))
-        MoveWindow(hWnd, TaskManagerSettings.Left, TaskManagerSettings.Top, TaskManagerSettings.Right - TaskManagerSettings.Left, TaskManagerSettings.Bottom - TaskManagerSettings.Top, TRUE);
-
-    if (TaskManagerSettings.Maximized)
-        ShowWindow(hWnd, SW_MAXIMIZE);
+    {
+        wp.length = sizeof(wp);
+        wp.flags = 0;
+        wp.showCmd = SW_SHOW;
+        wp.ptMinPosition.x = -1;
+        wp.ptMinPosition.y = -1;
+        wp.ptMaxPosition.x = -1;
+        wp.ptMaxPosition.y = -1;
+        wp.rcNormalPosition.left = TaskManagerSettings.Left;
+        wp.rcNormalPosition.top = TaskManagerSettings.Top;
+        wp.rcNormalPosition.right = TaskManagerSettings.Right;
+        wp.rcNormalPosition.bottom = TaskManagerSettings.Bottom;
+        SetWindowPlacement(hWnd, &wp);
+    }
 
     /* Set the always on top style */
     hMenu = GetMenu(hWnd);
@@ -318,7 +329,6 @@ static void LoadSettings(void)
     DWORD   dwSize;
 
     /* Window size & position settings */
-    TaskManagerSettings.Maximized = FALSE;
     TaskManagerSettings.Left = 0;
     TaskManagerSettings.Top = 0;
     TaskManagerSettings.Right = 0;
@@ -952,10 +962,6 @@ TaskManagerWndProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
         TaskManagerSettings.Top = wp.rcNormalPosition.top;
         TaskManagerSettings.Right = wp.rcNormalPosition.right;
         TaskManagerSettings.Bottom = wp.rcNormalPosition.bottom;
-        if (IsZoomed(hDlg) || (wp.flags & WPF_RESTORETOMAXIMIZED))
-            TaskManagerSettings.Maximized = TRUE;
-        else
-            TaskManagerSettings.Maximized = FALSE;
         return DefWindowProcW(hDlg, message, wParam, lParam);
 
     case WM_TIMER:
diff --git a/programs/taskmgr/taskmgr.h b/programs/taskmgr/taskmgr.h
index b8c383d5f4da..170b89f72e5f 100644
--- a/programs/taskmgr/taskmgr.h
+++ b/programs/taskmgr/taskmgr.h
@@ -51,7 +51,6 @@ typedef struct _IO_COUNTERS {
 typedef struct
 {
 	/* Window size & position settings */
-	BOOL	Maximized;
 	int	Left;
 	int	Top;
 	int	Right;
diff --git a/programs/wordpad/wordpad.c b/programs/wordpad/wordpad.c
index 428db1862e4f..2d97687ff440 100644
--- a/programs/wordpad/wordpad.c
+++ b/programs/wordpad/wordpad.c
@@ -2774,9 +2774,7 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hOldInstance, LPSTR szCmdPar
     HWND hRulerWnd;
     POINTL EditPoint;
     DWORD bMaximized;
-    MONITORINFO info;
-    HMONITOR monitor;
-    int x, y;
+    WINDOWPLACEMENT wp;
 
     InitCommonControlsEx(&classes);
 
@@ -2811,23 +2809,24 @@ int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hOldInstance, LPSTR szCmdPar
     RegisterClassExW(&wc);
 
     registry_read_winrect(&rc);
-    monitor = MonitorFromRect(&rc, MONITOR_DEFAULTTOPRIMARY);
-    info.cbSize = sizeof(info);
-    GetMonitorInfoW(monitor, &info);
-
-    x = rc.left;
-    y = rc.top;
-    IntersectRect(&info.rcWork, &info.rcWork, &rc);
-    if (IsRectEmpty(&info.rcWork))
-        x = y = CW_USEDEFAULT;
+    registry_read_maximized(&bMaximized);
 
     hMainWnd = CreateWindowExW(0, L"WORDPADTOP", L"Wine Wordpad", WS_CLIPCHILDREN|WS_OVERLAPPEDWINDOW,
-            x, y, rc.right - rc.left, rc.bottom - rc.top, NULL, NULL, hInstance, NULL);
-    registry_read_maximized(&bMaximized);
+                               CW_USEDEFAULT, CW_USEDEFAULT, rc.right - rc.left, rc.bottom - rc.top,
+                               NULL, NULL, hInstance, NULL);
     if ((nCmdShow == SW_SHOWNORMAL || nCmdShow == SW_SHOWDEFAULT)
 	     && bMaximized)
         nCmdShow = SW_SHOWMAXIMIZED;
-    ShowWindow(hMainWnd, nCmdShow);
+
+    wp.length = sizeof(wp);
+    wp.flags = 0;
+    wp.showCmd = nCmdShow;
+    wp.ptMinPosition.x = -1;
+    wp.ptMinPosition.y = -1;
+    wp.ptMaxPosition.x = -1;
+    wp.ptMaxPosition.y = -1;
+    wp.rcNormalPosition = rc;
+    SetWindowPlacement(hMainWnd, &wp);
 
     set_caption(NULL);
     set_bar_states();
diff --git a/server/protocol.def b/server/protocol.def
index 6c2ffc85aabe..30235dfe5fb8 100644
--- a/server/protocol.def
+++ b/server/protocol.def
@@ -267,6 +267,12 @@ struct property_data
     lparam_t       data;     /* data stored in property */
 };
 
+struct ratio
+{
+    unsigned short num;
+    unsigned short den;
+};
+
 /* structure to specify window rectangles */
 struct rectangle
 {
@@ -350,6 +356,7 @@ union hw_input
         unsigned int   flags;   /* event flags */
         unsigned int   time;    /* event time */
         lparam_t       info;    /* extra info */
+        unsigned int   raw_count; /* number of rawinput POINT in varargs */
     } mouse;
     struct
     {
@@ -906,8 +913,9 @@ struct monitor_info
 {
     struct rectangle raw;     /* host / physical position of the monitor rect */
     struct rectangle virt;    /* client / virtual position of the monitor rect */
-    unsigned int     flags;  /* MONITOR_FLAG_* flags for the monitor */
-    unsigned int     dpi;    /* physical DPI for the monitor */
+    unsigned int     flags;   /* MONITOR_FLAG_* flags for the monitor */
+    struct ratio     dpi;     /* effective DPI for the monitor */
+    struct ratio     raw_dpi; /* raw DPI for the monitor */
 };
 #define MONITOR_FLAG_PRIMARY  0x01
 #define MONITOR_FLAG_CLONE    0x02
@@ -1078,6 +1086,8 @@ typedef volatile struct
     unsigned int         fnid;             /* builtin class FNID, or 0 */
     unsigned int         ansi;             /* window wndproc is ansi */
     int                  __pad;
+    struct ratio         dpi;              /* effective DPI of the window monitor */
+    struct ratio         raw_dpi;          /* raw DPI of the window monitor */
     data_size_t          private_size;     /* length of private extra bytes range */
     data_size_t          extra_size;       /* size of the extra info */
     struct window_info   info;             /* window info (GWLP_*) */
@@ -2409,7 +2419,7 @@ enum message_type
     user_handle_t   win;       /* window handle */
     union hw_input  input;     /* input data */
     unsigned int    flags;     /* flags (see below) */
-    VARARG(report,bytes);      /* HID report data */
+    VARARG(extra,bytes);       /* HID report data / raw input data */
 @REPLY
     int             wait;      /* do we need to wait for a reply? */
     int             prev_x;    /* previous cursor position */
@@ -2417,7 +2427,8 @@ enum message_type
     int             new_x;     /* new cursor position */
     int             new_y;
 @END
-#define SEND_HWMSG_INJECTED    0x01
+#define SEND_HWMSG_INJECTED    0x01  /* message is injected from application */
+#define SEND_HWMSG_RAWINPUT    0x02  /* don't generate WM_INPUT x / y updates */
 
 
 /* Get a message from the current queue */
@@ -2765,7 +2776,7 @@ enum message_type
     user_handle_t  parent;        /* parent window */
     int            x;             /* point in parent coordinates */
     int            y;
-    int            dpi;           /* dpi for the point coordinates */
+    struct ratio   dpi;           /* dpi for the point coordinates */
 @REPLY
     int            count;         /* total count of children */
     VARARG(children,user_handles); /* children handles */
@@ -2790,7 +2801,6 @@ enum message_type
 @REQ(set_window_pos)
     unsigned short swp_flags;     /* SWP_* flags */
     unsigned short paint_flags;   /* paint flags (see below) */
-    unsigned int   monitor_dpi;   /* DPI of the window's monitor */
     user_handle_t  handle;        /* handle to the window */
     user_handle_t  previous;      /* previous window in Z order */
     struct rectangle window;      /* window rectangle (in parent coords) */
@@ -2809,7 +2819,7 @@ enum message_type
 @REQ(get_window_rectangles)
     user_handle_t  handle;        /* handle to the window */
     int            relative;      /* coords relative to (see below) */
-    int            dpi;           /* DPI to map to, or zero for per-monitor DPI */
+    struct ratio   dpi;           /* DPI to map to, or zero for per-monitor DPI */
 @REPLY
     struct rectangle window;      /* window rectangle */
     struct rectangle client;      /* client rectangle */
@@ -2843,7 +2853,7 @@ enum coords_relative
 @REQ(get_windows_offset)
     user_handle_t  from;          /* handle to the first window */
     user_handle_t  to;            /* handle to the second window */
-    int            dpi;           /* DPI to map to, or zero for per-monitor DPI */
+    struct ratio   dpi;           /* DPI to map to, or zero for per-monitor DPI */
 @REPLY
     int            x;             /* x coordinate offset */
     int            y;             /* y coordinate offset */
diff --git a/server/queue.c b/server/queue.c
index a04b53b70ded..437c4fa99713 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -590,7 +590,7 @@ static void set_cursor_pos( struct desktop *desktop, int x, int y )
         return;
     }
 
-    if (!(msg = alloc_hardware_message( 0, source, get_tick_count(), 0 ))) return;
+    if (!(msg = alloc_hardware_message( 0xff515700, source, get_tick_count(), 0 ))) return;
 
     msg->msg = WM_MOUSEMOVE;
     msg->x   = x;
@@ -2020,7 +2020,7 @@ static int is_current_process_foreground( struct desktop *desktop )
 #define WINE_MOUSE_HANDLE 1
 #define WINE_KEYBOARD_HANDLE 2
 
-static void rawmouse_init( struct rawinput *header, RAWMOUSE *rawmouse, int x, int y, unsigned int flags,
+static bool rawmouse_init( struct rawinput *header, RAWMOUSE *rawmouse, int x, int y, unsigned int flags,
                            unsigned int buttons, lparam_t info )
 {
     static const unsigned int button_flags[] =
@@ -2072,6 +2072,8 @@ static void rawmouse_init( struct rawinput *header, RAWMOUSE *rawmouse, int x, i
     rawmouse->lLastX             = x;
     rawmouse->lLastY             = y;
     rawmouse->ulExtraInformation = info;
+
+    return x || y || rawmouse->usButtonFlags;
 }
 
 static void rawkeyboard_init( struct rawinput *rawinput, RAWKEYBOARD *keyboard, unsigned short scan, unsigned short vkey,
@@ -2142,9 +2144,9 @@ struct rawinput_message
 };
 
 /* check if process is supposed to receive a WM_INPUT message and eventually queue it */
-static void queue_rawinput_message( struct desktop *desktop, struct process *process, void *args )
+static void queue_rawinput_message( struct desktop *desktop, struct process *process,
+                                    const struct rawinput_message *raw_msg )
 {
-    const struct rawinput_message *raw_msg = args;
     const struct rawinput_device *device;
     struct hardware_msg_data *msg_data;
     struct message *msg;
@@ -2201,7 +2203,7 @@ static void queue_rawinput_message( struct desktop *desktop, struct process *pro
     queue_hardware_message( desktop, msg, 1 );
 }
 
-static void dispatch_rawinput_message( struct desktop *desktop, struct rawinput_message *raw_msg )
+static void dispatch_rawinput_message( struct desktop *desktop, const struct rawinput_message *raw_msg )
 {
     struct process *process;
 
@@ -2211,8 +2213,10 @@ static void dispatch_rawinput_message( struct desktop *desktop, struct rawinput_
 
 /* queue a hardware message for a mouse event */
 static int queue_mouse_message( struct desktop *desktop, user_handle_t win, const union hw_input *input,
-                                unsigned int origin, struct msg_queue *sender )
+                                unsigned int origin, struct msg_queue *sender, bool rawinput )
 {
+    static const POINT empty_raw = {0};
+
     desktop_shm_t *desktop_shm = desktop->shared;
     struct hardware_msg_data *msg_data;
     struct rawinput_message raw_msg;
@@ -2221,6 +2225,7 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons
     unsigned int i, time = get_tick_count(), flags;
     struct hw_msg_source source = { IMDT_MOUSE, origin };
     lparam_t wparam = input->mouse.data << 16;
+    const POINT *raw = &empty_raw;
     int wait = 0, x, y;
 
     static const unsigned int messages[] =
@@ -2240,6 +2245,13 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons
         WM_MOUSEHWHEEL   /* 0x1000 = MOUSEEVENTF_HWHEEL */
     };
 
+    if (input->mouse.raw_count != get_req_data_size() / sizeof(*raw))
+    {
+        set_error( STATUS_INVALID_PARAMETER );
+        return 0;
+    }
+    if (input->mouse.raw_count) raw = get_req_data();
+
     SHARED_WRITE_BEGIN( desktop_shm, desktop_shm_t )
     {
         shared->cursor.last_change = time;
@@ -2250,6 +2262,16 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons
     time  = input->mouse.time;
     if (!time) time = desktop_shm->cursor.last_change;
 
+    if (win && origin == IMO_HARDWARE && flags == (MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE))
+    {
+        struct rectangle rect = { input->mouse.x, input->mouse.y, input->mouse.x + 1, input->mouse.y + 1 };
+        unsigned char state = (desktop_shm->keystate[VK_LBUTTON] | desktop_shm->keystate[VK_MBUTTON] |
+                               desktop_shm->keystate[VK_RBUTTON] | desktop_shm->keystate[VK_XBUTTON1] |
+                               desktop_shm->keystate[VK_XBUTTON2]) & 0x80;
+        input_shm_t *input_shm = sender && sender->input ? sender->input->shared : NULL;
+        if (!state && input_shm && !input_shm->capture) set_window_rect_visible( win, rect );
+    }
+
     if (flags & MOUSEEVENTF_MOVE)
     {
         if (flags & MOUSEEVENTF_ABSOLUTE)
@@ -2279,10 +2301,19 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons
         raw_msg.time       = time;
         raw_msg.message    = WM_INPUT;
         raw_msg.flags      = flags;
-        rawmouse_init( &raw_msg.rawinput, &raw_msg.data.mouse, x - desktop_shm->cursor.x, y - desktop_shm->cursor.y,
-                       raw_msg.flags, input->mouse.data, input->mouse.info );
 
-        dispatch_rawinput_message( desktop, &raw_msg );
+        for (int i = 0, count = max( 1, input->mouse.raw_count ); i < count; i++)
+        {
+            int raw_x, raw_y;
+
+            raw_x = rawinput ? raw[i].x : x - desktop_shm->cursor.x;
+            raw_y = rawinput ? raw[i].y : y - desktop_shm->cursor.y;
+
+            if (rawmouse_init( &raw_msg.rawinput, &raw_msg.data.mouse, raw_x, raw_y,
+                               raw_msg.flags, input->mouse.data, input->mouse.info ))
+                dispatch_rawinput_message( desktop, &raw_msg );
+        }
+
         release_object( foreground );
     }
 
@@ -3239,6 +3270,7 @@ DECL_HANDLER(send_hardware_message)
     struct desktop *desktop;
     unsigned int origin = (req->flags & SEND_HWMSG_INJECTED ? IMO_INJECTED : IMO_HARDWARE);
     struct msg_queue *sender = req->flags & SEND_HWMSG_INJECTED ? get_current_queue() : NULL;
+    bool rawinput = !!(req->flags & SEND_HWMSG_RAWINPUT);
     desktop_shm_t *desktop_shm;
     int wait = 0;
 
@@ -3258,7 +3290,7 @@ DECL_HANDLER(send_hardware_message)
     switch (req->input.type)
     {
     case INPUT_MOUSE:
-        wait = queue_mouse_message( desktop, req->win, &req->input, origin, sender );
+        wait = queue_mouse_message( desktop, req->win, &req->input, origin, sender, rawinput );
         break;
     case INPUT_KEYBOARD:
         wait = queue_keyboard_message( desktop, req->win, &req->input, origin, sender, 0 );
diff --git a/server/region.c b/server/region.c
index 9b0b1c66402d..d77038c161f5 100644
--- a/server/region.c
+++ b/server/region.c
@@ -741,7 +741,7 @@ void mirror_region( const struct rectangle *client_rect, struct region *region )
 
 
 /* scale a region for a given dpi factor */
-void scale_region( struct region *region, unsigned int dpi_from, unsigned int dpi_to )
+void scale_region( struct region *region, struct ratio dpi_from, struct ratio dpi_to )
 {
     struct rectangle *rect, *end;
 
diff --git a/server/request_handlers.h b/server/request_handlers.h
index 03cf907c19c3..82797824f0a2 100644
--- a/server/request_handlers.h
+++ b/server/request_handlers.h
@@ -660,6 +660,7 @@ C_ASSERT( sizeof(struct object_type_info) == 44 );
 C_ASSERT( sizeof(struct pe_image_info) == 96 );
 C_ASSERT( sizeof(struct process_info) == 40 );
 C_ASSERT( sizeof(struct property_data) == 16 );
+C_ASSERT( sizeof(struct ratio) == 4 );
 C_ASSERT( sizeof(struct rawinput_device) == 12 );
 C_ASSERT( sizeof(struct rectangle) == 16 );
 C_ASSERT( sizeof(struct startup_info_data) == 96 );
@@ -1577,12 +1578,11 @@ C_ASSERT( offsetof(struct get_window_tree_reply, last_child) == 36 );
 C_ASSERT( sizeof(struct get_window_tree_reply) == 40 );
 C_ASSERT( offsetof(struct set_window_pos_request, swp_flags) == 12 );
 C_ASSERT( offsetof(struct set_window_pos_request, paint_flags) == 14 );
-C_ASSERT( offsetof(struct set_window_pos_request, monitor_dpi) == 16 );
-C_ASSERT( offsetof(struct set_window_pos_request, handle) == 20 );
-C_ASSERT( offsetof(struct set_window_pos_request, previous) == 24 );
-C_ASSERT( offsetof(struct set_window_pos_request, window) == 28 );
-C_ASSERT( offsetof(struct set_window_pos_request, client) == 44 );
-C_ASSERT( sizeof(struct set_window_pos_request) == 64 );
+C_ASSERT( offsetof(struct set_window_pos_request, handle) == 16 );
+C_ASSERT( offsetof(struct set_window_pos_request, previous) == 20 );
+C_ASSERT( offsetof(struct set_window_pos_request, window) == 24 );
+C_ASSERT( offsetof(struct set_window_pos_request, client) == 40 );
+C_ASSERT( sizeof(struct set_window_pos_request) == 56 );
 C_ASSERT( offsetof(struct set_window_pos_reply, new_style) == 8 );
 C_ASSERT( offsetof(struct set_window_pos_reply, new_ex_style) == 12 );
 C_ASSERT( offsetof(struct set_window_pos_reply, surface_win) == 16 );
diff --git a/server/request_trace.h b/server/request_trace.h
index 7d1a1c406d4e..497c88e6c2fe 100644
--- a/server/request_trace.h
+++ b/server/request_trace.h
@@ -14,6 +14,7 @@ static void dump_ioctl_code( const char *prefix, const ioctl_code_t *val );
 static void dump_irp_params( const char *prefix, const union irp_params *val );
 static void dump_luid( const char *prefix, const struct luid *val );
 static void dump_obj_locator( const char *prefix, const struct obj_locator *val );
+static void dump_ratio( const char *prefix, const struct ratio *val );
 static void dump_rectangle( const char *prefix, const struct rectangle *val );
 static void dump_timeout( const char *prefix, const timeout_t *val );
 static void dump_uint64( const char *prefix, const unsigned __int64 *val );
@@ -1451,7 +1452,7 @@ static void dump_send_hardware_message_request( const struct send_hardware_messa
     fprintf( stderr, " win=%08x", req->win );
     dump_hw_input( ", input=", &req->input );
     fprintf( stderr, ", flags=%08x", req->flags );
-    dump_varargs_bytes( ", report=", cur_size );
+    dump_varargs_bytes( ", extra=", cur_size );
 }
 
 static void dump_send_hardware_message_reply( const struct send_hardware_message_reply *req )
@@ -1832,7 +1833,7 @@ static void dump_get_window_children_from_point_request( const struct get_window
     fprintf( stderr, " parent=%08x", req->parent );
     fprintf( stderr, ", x=%d", req->x );
     fprintf( stderr, ", y=%d", req->y );
-    fprintf( stderr, ", dpi=%d", req->dpi );
+    dump_ratio( ", dpi=", &req->dpi );
 }
 
 static void dump_get_window_children_from_point_reply( const struct get_window_children_from_point_reply *req )
@@ -1862,7 +1863,6 @@ static void dump_set_window_pos_request( const struct set_window_pos_request *re
 {
     fprintf( stderr, " swp_flags=%04x", req->swp_flags );
     fprintf( stderr, ", paint_flags=%04x", req->paint_flags );
-    fprintf( stderr, ", monitor_dpi=%08x", req->monitor_dpi );
     fprintf( stderr, ", handle=%08x", req->handle );
     fprintf( stderr, ", previous=%08x", req->previous );
     dump_rectangle( ", window=", &req->window );
@@ -1881,7 +1881,7 @@ static void dump_get_window_rectangles_request( const struct get_window_rectangl
 {
     fprintf( stderr, " handle=%08x", req->handle );
     fprintf( stderr, ", relative=%d", req->relative );
-    fprintf( stderr, ", dpi=%d", req->dpi );
+    dump_ratio( ", dpi=", &req->dpi );
 }
 
 static void dump_get_window_rectangles_reply( const struct get_window_rectangles_reply *req )
@@ -1911,7 +1911,7 @@ static void dump_get_windows_offset_request( const struct get_windows_offset_req
 {
     fprintf( stderr, " from=%08x", req->from );
     fprintf( stderr, ", to=%08x", req->to );
-    fprintf( stderr, ", dpi=%d", req->dpi );
+    dump_ratio( ", dpi=", &req->dpi );
 }
 
 static void dump_get_windows_offset_reply( const struct get_windows_offset_reply *req )
diff --git a/server/trace.c b/server/trace.c
index b02e08fce03c..0731063f8e3e 100644
--- a/server/trace.c
+++ b/server/trace.c
@@ -173,6 +173,11 @@ static void dump_rectangle( const char *prefix, const struct rectangle *rect )
              rect->left, rect->top, rect->right, rect->bottom );
 }
 
+static void dump_ratio( const char *prefix, const struct ratio *q )
+{
+    fprintf( stderr, "%s{%d:%d}", prefix, q->num, q->den );
+}
+
 static void dump_ioctl_code( const char *prefix, const ioctl_code_t *code )
 {
     switch(*code)
@@ -475,7 +480,7 @@ static void dump_hw_input( const char *prefix, const union hw_input *input )
                  prefix, input->mouse.x, input->mouse.y, input->mouse.data, input->mouse.flags,
                  input->mouse.time );
         dump_uint64( ",info=", &input->mouse.info );
-        fputc( '}', stderr );
+        fprintf( stderr, ",raw_count=%u}", input->mouse.raw_count );
         break;
     case INPUT_KEYBOARD:
         fprintf( stderr, "%s{type=KEYBOARD,vkey=%04hx,scan=%04hx,flags=%08x,time=%u",
@@ -1646,7 +1651,8 @@ static void dump_varargs_monitor_infos( const char *prefix, data_size_t size )
     {
         dump_rectangle( "{raw:", &monitor->virt );
         dump_rectangle( ",virt:", &monitor->virt );
-        fprintf( stderr, ",flags:%#x,dpi:%u", monitor->flags, monitor->dpi );
+        fprintf( stderr, ",flags:%#x", monitor->flags );
+        dump_ratio( ",dpi:", &monitor->dpi );
         fputc( '}', stderr );
         if (--len) fputc( ',', stderr );
     }
diff --git a/server/user.h b/server/user.h
index 1d16e2aab950..d146d0cf8cf5 100644
--- a/server/user.h
+++ b/server/user.h
@@ -152,7 +152,7 @@ extern int is_region_equal( const struct region *region1, const struct region *r
 extern void get_region_extents( const struct region *region, struct rectangle *rect );
 extern void offset_region( struct region *region, int x, int y );
 extern void mirror_region( const struct rectangle *client_rect, struct region *region );
-extern void scale_region( struct region *region, unsigned int dpi_from, unsigned int dpi_to );
+extern void scale_region( struct region *region, struct ratio dpi_from, struct ratio dpi_to );
 extern struct region *copy_region( struct region *dst, const struct region *src );
 extern struct region *intersect_region( struct region *dst, const struct region *src1,
                                         const struct region *src2 );
@@ -184,6 +184,7 @@ extern user_handle_t shallow_window_from_point( struct desktop *desktop, int x,
 extern struct thread *window_thread_from_point( user_handle_t scope, int x, int y );
 extern user_handle_t find_window_to_repaint( user_handle_t parent, struct thread *thread );
 extern struct window_class *get_window_class( user_handle_t window );
+extern void set_window_rect_visible( user_handle_t window, struct rectangle rect );
 
 /* window class functions */
 
@@ -231,13 +232,14 @@ static inline int point_in_rect( const struct rectangle *rect, int x, int y )
     return (x >= rect->left && x < rect->right && y >= rect->top && y < rect->bottom);
 }
 
-static inline int scale_dpi( int val, unsigned int dpi_from, unsigned int dpi_to )
+static inline int scale_dpi( int val, struct ratio dpi_from, struct ratio dpi_to )
 {
-    if (val >= 0) return (val * dpi_to + (dpi_from / 2)) / dpi_from;
-    return (val * dpi_to - (dpi_from / 2)) / dpi_from;
+    unsigned int num = dpi_to.num * dpi_from.den, den = dpi_from.num * dpi_to.den;
+    if (val >= 0) return (val * num + (den / 2)) / den;
+    return (val * num - (den / 2)) / den;
 }
 
-static inline void scale_dpi_rect( struct rectangle *rect, unsigned int dpi_from, unsigned int dpi_to )
+static inline void scale_dpi_rect( struct rectangle *rect, struct ratio dpi_from, struct ratio dpi_to )
 {
     rect->left   = scale_dpi( rect->left, dpi_from, dpi_to );
     rect->top    = scale_dpi( rect->top, dpi_from, dpi_to );
diff --git a/server/window.c b/server/window.c
index 6bff48e7a220..73bad9007061 100644
--- a/server/window.c
+++ b/server/window.c
@@ -36,6 +36,8 @@
 #include "user.h"
 #include "unicode.h"
 
+static const struct ratio no_dpi;
+
 /* a window property */
 struct property
 {
@@ -81,7 +83,6 @@ struct window
     unsigned int     color_key;       /* color key for a layered window */
     unsigned int     alpha;           /* alpha value for a layered window */
     unsigned int     layered_flags;   /* flags for a layered window */
-    unsigned int     monitor_dpi;     /* DPI of the window monitor */
     WCHAR           *text;            /* window caption text */
     data_size_t      text_len;        /* length of window caption */
     unsigned int     paint_flags;     /* various painting flags */
@@ -319,16 +320,18 @@ static void map_point_raw_to_virt( struct desktop *desktop, int *x, int *y )
 }
 
 /* get the per-monitor DPI for a window */
-static unsigned int get_monitor_dpi( struct window *win )
+static struct ratio get_monitor_dpi( struct window *win )
 {
     while (win->parent && !is_desktop_window( win->parent )) win = win->parent;
-    return win->monitor_dpi;
+    return win->shared->dpi;
 }
 
-static unsigned int get_window_dpi( struct window *win )
+static struct ratio get_window_dpi( struct window *win )
 {
+    struct ratio dpi = {1, 1};
     if (NTUSER_DPI_CONTEXT_IS_MONITOR_AWARE( win->shared->dpi_context )) return get_monitor_dpi( win );
-    return NTUSER_DPI_CONTEXT_GET_DPI( win->shared->dpi_context );
+    dpi.num = NTUSER_DPI_CONTEXT_GET_DPI( win->shared->dpi_context );
+    return dpi;
 }
 
 /* link a window at the right place in the siblings list */
@@ -390,6 +393,20 @@ static int link_window( struct window *win, struct window *previous )
     return old_prev != win->entry.prev;
 }
 
+static void set_window_monitor_dpi( struct window *win )
+{
+    struct monitor_info *info;
+
+    if (!(info = get_monitor_from_rect( win->desktop->winstation, &win->window_rect, 0 ))) return;
+
+    SHARED_WRITE_BEGIN( win->shared, window_shm_t )
+    {
+        shared->dpi     = info->dpi;
+        shared->raw_dpi = info->raw_dpi;
+    }
+    SHARED_WRITE_END;
+}
+
 /* change the parent of a window (or unlink the window if the new parent is NULL) */
 static int set_parent_window( struct window *win, struct window *parent )
 {
@@ -411,14 +428,14 @@ static int set_parent_window( struct window *win, struct window *parent )
         win->parent = (struct window *)grab_object( parent );
         link_window( win, WINPTR_TOP );
 
-        if (!is_desktop_window( parent ))
+        if (is_desktop_window( parent )) set_window_monitor_dpi( win );
+        else SHARED_WRITE_BEGIN( win->shared, window_shm_t )
         {
-            SHARED_WRITE_BEGIN( win->shared, window_shm_t )
-            {
-                shared->dpi_context = parent->shared->dpi_context;
-            }
-            SHARED_WRITE_END;
+            shared->dpi_context = parent->shared->dpi_context;
+            shared->dpi         = parent->shared->dpi;
+            shared->raw_dpi     = parent->shared->raw_dpi;
         }
+        SHARED_WRITE_END;
 
         /* if parent belongs to a different thread and the window isn't */
         /* top-level, attach the two threads */
@@ -658,7 +675,6 @@ static struct window *create_window( struct window *parent, struct window *owner
     win->is_layered     = 0;
     win->is_orphan      = 0;
     win->set_foreground = 0;
-    win->monitor_dpi    = USER_DEFAULT_SCREEN_DPI;
     win->text           = NULL;
     win->text_len       = 0;
     win->paint_flags    = 0;
@@ -674,9 +690,14 @@ static struct window *create_window( struct window *parent, struct window *owner
     SHARED_WRITE_BEGIN( win->shared, window_shm_t )
     {
         shared->class           = class_locator;
+        /* FIXME: NTUSER_DPI_PER_MONITOR_AWARE_V2 isn't implemented */
         shared->dpi_context     = NTUSER_DPI_PER_MONITOR_AWARE;
         shared->fnid            = fnid;
         shared->private_size    = private_size;
+        shared->dpi.num         = USER_DEFAULT_SCREEN_DPI;
+        shared->dpi.den         = 1;
+        shared->raw_dpi.num     = USER_DEFAULT_SCREEN_DPI;
+        shared->raw_dpi.den     = 1;
         shared->extra_size      = extra_size;
         memset( (void *)&shared->info, 0, sizeof(shared->info) );
         memset( (void *)shared->extra, 0, extra_size );
@@ -815,30 +836,30 @@ static inline void inc_window_paint_count( struct window *win, int incr )
 }
 
 /* map a point between different DPI scaling levels */
-static void map_dpi_point( struct window *win, int *x, int *y, unsigned int from, unsigned int to )
+static void map_dpi_point( struct window *win, int *x, int *y, struct ratio from, struct ratio to )
 {
-    if (!from) from = get_monitor_dpi( win );
-    if (!to) to = get_monitor_dpi( win );
-    if (from == to) return;
+    if (!from.num) from = get_monitor_dpi( win );
+    if (!to.num) to = get_monitor_dpi( win );
+    if (from.num == to.num) return;
     *x = scale_dpi( *x, from, to );
     *y = scale_dpi( *y, from, to );
 }
 
 /* map a window rectangle between different DPI scaling levels */
-static void map_dpi_rect( struct window *win, struct rectangle *rect, unsigned int from, unsigned int to )
+static void map_dpi_rect( struct window *win, struct rectangle *rect, struct ratio from, struct ratio to )
 {
-    if (!from) from = get_monitor_dpi( win );
-    if (!to) to = get_monitor_dpi( win );
-    if (from == to) return;
+    if (!from.num) from = get_monitor_dpi( win );
+    if (!to.num) to = get_monitor_dpi( win );
+    if (from.num == to.num) return;
     scale_dpi_rect( rect, from, to );
 }
 
 /* map a region between different DPI scaling levels */
-static void map_dpi_region( struct window *win, struct region *region, unsigned int from, unsigned int to )
+static void map_dpi_region( struct window *win, struct region *region, struct ratio from, struct ratio to )
 {
-    if (!from) from = get_monitor_dpi( win );
-    if (!to) to = get_monitor_dpi( win );
-    if (from == to) return;
+    if (!from.num) from = get_monitor_dpi( win );
+    if (!to.num) to = get_monitor_dpi( win );
+    if (from.num == to.num) return;
     scale_region( region, from, to );
 }
 
@@ -853,7 +874,7 @@ static inline void client_to_screen( struct window *win, int *x, int *y )
 }
 
 /* convert coordinates from screen to client coords and dpi */
-static void screen_to_client( struct window *win, int *x, int *y, unsigned int dpi )
+static void screen_to_client( struct window *win, int *x, int *y, struct ratio dpi )
 {
     int offset_x = 0, offset_y = 0;
 
@@ -909,7 +930,7 @@ static int is_parent_composited( struct window *win )
 }
 
 /* check if point is inside the window, and map to window dpi */
-static int is_point_in_window( struct window *win, int *x, int *y, unsigned int dpi )
+static int is_point_in_window( struct window *win, int *x, int *y, struct ratio dpi )
 {
     if (!(win->style & WS_VISIBLE)) return 0; /* not visible */
     if ((win->style & (WS_POPUP|WS_CHILD|WS_DISABLED)) == (WS_CHILD|WS_DISABLED))
@@ -1038,7 +1059,7 @@ user_handle_t shallow_window_from_point( struct desktop *desktop, int x, int y )
     {
         int x_child = x, y_child = y;
 
-        if (!is_point_in_window( ptr, &x_child, &y_child, 0 )) continue;  /* skip it */
+        if (!is_point_in_window( ptr, &x_child, &y_child, no_dpi )) continue;  /* skip it */
         return ptr->handle;
     }
     return desktop->top_window->handle;
@@ -1053,14 +1074,14 @@ struct thread *window_thread_from_point( user_handle_t scope, int x, int y )
 
     map_point_raw_to_virt( win->desktop, &x, &y );
 
-    screen_to_client( win, &x, &y, 0 );
+    screen_to_client( win, &x, &y, no_dpi );
     win = child_window_from_point( win, x, y );
     if (!win->thread) return NULL;
     return (struct thread *)grab_object( win->thread );
 }
 
 /* return list of all windows containing point (in absolute coords) */
-static int all_windows_from_point( struct window *top, int x, int y, unsigned int dpi,
+static int all_windows_from_point( struct window *top, int x, int y, struct ratio dpi,
                                    struct user_handle_array *array )
 {
     if (!is_desktop_window( top ) && !is_desktop_window( top->parent ))
@@ -1946,6 +1967,9 @@ static void set_window_pos( struct window *win, struct window *previous,
     if (swp_flags & SWP_SHOWWINDOW) win->style |= WS_VISIBLE;
     else if (swp_flags & SWP_HIDEWINDOW) win->style &= ~WS_VISIBLE;
 
+    /* update window monitor dpi for toplevel windows */
+    if (!win->parent || is_desktop_window( win->parent )) set_window_monitor_dpi( win );
+
     /* keep children at the same position relative to top right corner when the parent is mirrored */
     if (win->ex_style & WS_EX_LAYOUTRTL)
     {
@@ -2204,7 +2228,7 @@ DECL_HANDLER(create_window)
     struct window *win, *parent = NULL, *owner = NULL;
     struct unicode_str cls_name = get_req_unicode_str();
     struct atom_table *table = get_user_atom_table();
-    unsigned int dpi_context;
+    unsigned int dpi_context = req->dpi_context;
     atom_t atom = req->atom;
 
     reply->handle = 0;
@@ -2237,17 +2261,19 @@ DECL_HANDLER(create_window)
 
     if (!(win = create_window( parent, owner, atom, req->class_instance, !!req->ansi ))) return;
 
-    if (parent && !is_desktop_window( parent ))
-        dpi_context = parent->shared->dpi_context;
-    else if (!parent || !NTUSER_DPI_CONTEXT_IS_MONITOR_AWARE( req->dpi_context ))
-        dpi_context = req->dpi_context;
-    else
-        dpi_context = win->shared->dpi_context;
+    /* FIXME: NTUSER_DPI_PER_MONITOR_AWARE_V2 isn't implemented */
+    if (NTUSER_DPI_CONTEXT_IS_MONITOR_AWARE( dpi_context )) dpi_context = NTUSER_DPI_PER_MONITOR_AWARE;
 
     SHARED_WRITE_BEGIN( win->shared, window_shm_t )
     {
         shared->dpi_context     = dpi_context;
         shared->info.instance   = req->instance;
+        if (parent && !is_desktop_window( parent ))
+        {
+            shared->dpi_context = parent->shared->dpi_context;
+            shared->dpi         = parent->shared->dpi;
+            shared->raw_dpi     = parent->shared->raw_dpi;
+        }
     }
     SHARED_WRITE_END;
 
@@ -2732,7 +2758,6 @@ DECL_HANDLER(set_window_pos)
     win->paint_flags = (win->paint_flags & ~PAINT_CLIENT_FLAGS) | (req->paint_flags & PAINT_CLIENT_FLAGS);
     if (win->paint_flags & PAINT_HAS_PIXEL_FORMAT) update_pixel_format_flags( win );
 
-    win->monitor_dpi = req->monitor_dpi;
     old_style = win->style;
     old_window = win->window_rect;
     old_client = win->client_rect;
@@ -3031,12 +3056,17 @@ DECL_HANDLER(get_update_region)
 
 
 /* update the z order of a window so that a given rectangle is fully visible */
-DECL_HANDLER(update_window_zorder)
+void set_window_rect_visible( user_handle_t window, struct rectangle rect )
 {
-    struct rectangle tmp, rect = req->rect;
-    struct window *ptr, *win = get_window( req->window );
+    struct window *ptr, *win;
+    struct rectangle tmp;
+
+    if (!(win = get_window( window )) || !win->parent || !is_visible( win )) return;  /* nothing to do */
 
-    if (!win || !win->parent || !is_visible( win )) return;  /* nothing to do */
+    map_point_raw_to_virt( win->desktop, &rect.left, &rect.top );
+    map_point_raw_to_virt( win->desktop, &rect.right, &rect.bottom );
+    rect.right = max( rect.left + 1, rect.right );
+    rect.bottom = max( rect.top + 1, rect.bottom );
 
     LIST_FOR_EACH_ENTRY( ptr, &win->parent->children, struct window, entry )
     {
@@ -3063,6 +3093,10 @@ DECL_HANDLER(update_window_zorder)
     }
 }
 
+DECL_HANDLER(update_window_zorder)
+{
+    set_window_rect_visible( req->window, req->rect );
+}
 
 /* mark parts of a window as needing a redraw */
 DECL_HANDLER(redraw_window)
diff --git a/tools/make_announce b/tools/make_announce
index ba829d050db5..002392e68231 100755
--- a/tools/make_announce
+++ b/tools/make_announce
@@ -24,7 +24,9 @@
 use strict;
 use locale;
 use POSIX;
-use Text::CSV::Encoded;
+use Encode;
+use JSON;
+use REST::Client;
 use open ':encoding(utf8)';
 
 sub unescape($)
@@ -70,20 +72,10 @@ sub get_current_version()
 sub get_bugs($)
 {
     my $filter = shift;
-    my $csv = Text::CSV::Encoded->new({ encoding_in => "utf-8", encoding_out => "utf-8" });
-    my %bugs;
-
-    open QUERY, "-|" or exec "wget", "-qO-", "https://bugs.winehq.org/buglist.cgi?columnlist=short_desc&query_format=advanced&ctype=csv&$filter"
-        or die "cannot query bug list";
-    <QUERY>;  # skip header line
-    while (<QUERY>)
-    {
-        next unless $csv->parse($_);
-        my ($id, $descr) = $csv->fields();
-        $bugs{$id} = $descr;
-    }
-    close QUERY;
-    return %bugs;
+    my $bz = REST::Client->new({ host => 'https://bugs.winehq.org/rest/' });
+    $bz->GET( "bug?include_fields=id,summary&$filter", { Accept => 'application/json' } );
+    my $bugs = from_json( decode_utf8( $bz->responseContent() ));
+    return map { $_->{id} => $_->{summary} } @{$bugs->{bugs}};
 }
 
 # retrieve the current list of authors
diff --git a/tools/make_requests b/tools/make_requests
index 7e317647a517..4b8139596a34 100755
--- a/tools/make_requests
+++ b/tools/make_requests
@@ -69,6 +69,7 @@ my %formats =
     "union udp_endpoint"       => [ 32, 4 ],
     "struct user_apc"          => [ 40, 8 ],
     "struct class_info"        => [ 56, 8 ],
+    "struct ratio"             => [  4, 4 ],
 );
 
 my $file_header =
