Compare commits

..

No commits in common. "0cc911f974075c78578851c7cccd420855227f00" and "0a3c76e6954a2b7194cb54b7d57db1b7afe7c43a" have entirely different histories.

73 changed files with 40538 additions and 473 deletions

View file

@ -1,38 +1,5 @@
{
"nodes": {
"agenix": {
"inputs": {
"darwin": [
"nix-community-infra",
"nix-darwin"
],
"home-manager": [
"nix-community-infra",
"empty"
],
"nixpkgs": [
"nix-community-infra",
"nixpkgs"
],
"systems": [
"nix-community-infra",
"systems"
]
},
"locked": {
"lastModified": 1718371084,
"narHash": "sha256-abpBi61mg0g+lFFU0zY4C6oP6fBwPzbHPKBGw676xsA=",
"owner": "ryantm",
"repo": "agenix",
"rev": "3a56735779db467538fb2e577eda28a9daacaca6",
"type": "github"
},
"original": {
"owner": "ryantm",
"repo": "agenix",
"type": "github"
}
},
"blobs": {
"flake": false,
"locked": {
@ -49,57 +16,6 @@
"type": "gitlab"
}
},
"buildbot-nix": {
"inputs": {
"flake-parts": [
"nix-community-infra",
"flake-parts"
],
"nixpkgs": [
"nix-community-infra",
"nixpkgs"
],
"treefmt-nix": [
"nix-community-infra",
"treefmt-nix"
]
},
"locked": {
"lastModified": 1719797756,
"narHash": "sha256-TGZthxgxLdT8boadFm6+MK7HZlIxN1u1V+x3hu+Fd8I=",
"owner": "nix-community",
"repo": "buildbot-nix",
"rev": "0b56574a5c823097771487d1bac952c3549fe9fb",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "buildbot-nix",
"type": "github"
}
},
"comin": {
"inputs": {
"nixpkgs": [
"nix-community-infra",
"nixpkgs"
]
},
"locked": {
"lastModified": 1714766748,
"narHash": "sha256-//fP25eh1f9eA+0TEgWqusWjv9SuKh8rIF1ZJQNL1jo=",
"owner": "nlewo",
"repo": "comin",
"rev": "d3658c452024824235de2355ac3e156b10c3eaaf",
"type": "github"
},
"original": {
"owner": "nlewo",
"repo": "comin",
"rev": "d3658c452024824235de2355ac3e156b10c3eaaf",
"type": "github"
}
},
"coop-fd": {
"inputs": {
"crane": "crane",
@ -143,42 +59,6 @@
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": [
"nix-community-infra",
"nixpkgs"
]
},
"locked": {
"lastModified": 1719733833,
"narHash": "sha256-6h2EqZU9bL9rHlXE+2LCBgnDImejzbS+4dYsNDDFlkY=",
"owner": "nix-community",
"repo": "disko",
"rev": "d185770ea261fb5cf81aa5ad1791b93a7834d12c",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"empty": {
"locked": {
"lastModified": 1708697125,
"narHash": "sha256-JeMK8G1oabQTSpqXhYaYtPRak4m6z1xxyRKf8CvHy14=",
"owner": "nix-systems",
"repo": "empty",
"rev": "23d743284b73ae69caf0cb7874edf05c0c631a1f",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "empty",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
@ -212,21 +92,6 @@
}
},
"flake-compat_3": {
"locked": {
"lastModified": 1717312683,
"narHash": "sha256-FrlieJH50AuvagamEvWMIE6D2OAnERuDboFDYAED/dE=",
"owner": "nix-community",
"repo": "flake-compat",
"rev": "38fd3954cf65ce6faf3d0d45cd26059e059f07ea",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "flake-compat",
"type": "github"
}
},
"flake-compat_4": {
"flake": false,
"locked": {
"lastModified": 1696426674,
@ -243,27 +108,6 @@
}
},
"flake-parts": {
"inputs": {
"nixpkgs-lib": [
"nix-community-infra",
"nixpkgs"
]
},
"locked": {
"lastModified": 1719745305,
"narHash": "sha256-xwgjVUpqSviudEkpQnioeez1Uo2wzrsMaJKJClh+Bls=",
"owner": "hercules-ci",
"repo": "flake-parts",
"rev": "c3c5ecc05edc7dafba779c6c1a61cd08ac6583e9",
"type": "github"
},
"original": {
"owner": "hercules-ci",
"repo": "flake-parts",
"type": "github"
}
},
"flake-parts_2": {
"inputs": {
"nixpkgs-lib": "nixpkgs-lib"
},
@ -283,25 +127,7 @@
},
"flake-utils": {
"inputs": {
"systems": "systems_3"
},
"locked": {
"lastModified": 1710146030,
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"flake-utils_2": {
"inputs": {
"systems": "systems_4"
"systems": "systems_2"
},
"locked": {
"lastModified": 1705309234,
@ -370,63 +196,9 @@
"type": "github"
}
},
"nix-community-infra": {
"inputs": {
"agenix": "agenix",
"buildbot-nix": "buildbot-nix",
"comin": "comin",
"disko": "disko",
"empty": "empty",
"flake-compat": "flake-compat_3",
"flake-parts": "flake-parts",
"nix-darwin": "nix-darwin",
"nixpkgs": "nixpkgs",
"nixpkgs-update": "nixpkgs-update",
"nixpkgs-update-github-releases": "nixpkgs-update-github-releases",
"nur-update": "nur-update",
"sops-nix": "sops-nix",
"srvos": "srvos",
"systems": "systems",
"treefmt-nix": "treefmt-nix"
},
"locked": {
"lastModified": 1719876004,
"narHash": "sha256-YIGAUeEVgJ/Qpn+YUoyR9HjV6j01W+5vOKRYpqsx+gw=",
"owner": "nix-community",
"repo": "infra",
"rev": "766a23a468858d2413f12bec828860158abaadf1",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "infra",
"type": "github"
}
},
"nix-darwin": {
"inputs": {
"nixpkgs": [
"nix-community-infra",
"nixpkgs"
]
},
"locked": {
"lastModified": 1719128254,
"narHash": "sha256-I7jMpq0CAOZA/i70+HDQO/ulLttyQu/K70cSESiMX7A=",
"owner": "LnL7",
"repo": "nix-darwin",
"rev": "50581970f37f06a4719001735828519925ef8310",
"type": "github"
},
"original": {
"owner": "LnL7",
"repo": "nix-darwin",
"type": "github"
}
},
"nix-gaming": {
"inputs": {
"flake-parts": "flake-parts_2",
"flake-parts": "flake-parts",
"nixpkgs": [
"nixpkgs"
]
@ -483,7 +255,7 @@
"nixos-mailserver": {
"inputs": {
"blobs": "blobs",
"flake-compat": "flake-compat_4",
"flake-compat": "flake-compat_3",
"nixpkgs": [
"nixpkgs"
],
@ -525,16 +297,15 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1719824438,
"narHash": "sha256-pY0wosAgcr9W4vmGML0T3BVhQiGuKoozCbs2t+Je1zc=",
"owner": "NixOS",
"lastModified": 1717922286,
"narHash": "sha256-RsV7rNWYceglPDVgVukwYfoYK2fuAgyvyvSE/Gyah+Y=",
"owner": "chayleaf",
"repo": "nixpkgs",
"rev": "7f993cdf26ccef564eabf31fdb40d140821e12bc",
"rev": "47c40a60d0952cd6b86c171898870142b0747602",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable-small",
"owner": "chayleaf",
"repo": "nixpkgs",
"type": "github"
}
@ -567,81 +338,6 @@
"url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz"
}
},
"nixpkgs-update": {
"inputs": {
"mmdoc": [
"nix-community-infra",
"empty"
],
"nixpkgs": "nixpkgs_2",
"runtimeDeps": [
"nix-community-infra",
"nixpkgs"
],
"treefmt-nix": [
"nix-community-infra",
"treefmt-nix"
]
},
"locked": {
"lastModified": 1719362107,
"narHash": "sha256-Vrd+Y3odIIXjlUwSDOjyrhf6V4svB5njtGH9i0NJIxc=",
"owner": "nix-community",
"repo": "nixpkgs-update",
"rev": "d49fec1752e0bcb69f156a86ba5b93e14aaca2c3",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs-update",
"type": "github"
}
},
"nixpkgs-update-github-releases": {
"flake": false,
"locked": {
"lastModified": 1713252683,
"narHash": "sha256-/hWsx6qCMHIRgdGkEIHaI40eVUg5gQGxpfp6GG7moSU=",
"owner": "nix-community",
"repo": "nixpkgs-update-github-releases",
"rev": "e1a72b5848a3a08168917dee85a6b4b64eca6b24",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nixpkgs-update-github-releases",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1672428209,
"narHash": "sha256-eejhqkDz2cb2vc5VeaWphJz8UXNuoNoM8/Op8eWv2tQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "293a28df6d7ff3dec1e61e37cc4ee6e6c0fb0847",
"type": "github"
},
"original": {
"id": "nixpkgs",
"type": "indirect"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1717922286,
"narHash": "sha256-RsV7rNWYceglPDVgVukwYfoYK2fuAgyvyvSE/Gyah+Y=",
"owner": "chayleaf",
"repo": "nixpkgs",
"rev": "47c40a60d0952cd6b86c171898870142b0747602",
"type": "github"
},
"original": {
"owner": "chayleaf",
"repo": "nixpkgs",
"type": "github"
}
},
"notlua": {
"inputs": {
"nixpkgs": [
@ -697,48 +393,6 @@
"type": "github"
}
},
"nur-update": {
"inputs": {
"nixpkgs": [
"nix-community-infra",
"nixpkgs"
]
},
"locked": {
"lastModified": 1716222588,
"narHash": "sha256-gbyIIHsJpk3V8tnsGP8RZpgc0NM/R9QT4zk4SObRDas=",
"owner": "nix-community",
"repo": "nur-update",
"rev": "a54c4c1b0ec643ff36b3c9deecb5fefce57dcabc",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "nur-update",
"type": "github"
}
},
"osu-wine": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1720010227,
"narHash": "sha256-ADhjhE6UMjcCgjycONnabDqJ7WR2O4SpoAYvmScLwgc=",
"owner": "chayleaf",
"repo": "osu-wine.nix",
"rev": "1406bb2c6ee6eaa8c1433516ef5d324eb651c3fc",
"type": "github"
},
"original": {
"owner": "chayleaf",
"repo": "osu-wine.nix",
"type": "github"
}
},
"root": {
"inputs": {
"coop-fd": "coop-fd",
@ -746,24 +400,22 @@
"home-manager": "home-manager",
"impermanence": "impermanence",
"mobile-nixos": "mobile-nixos",
"nix-community-infra": "nix-community-infra",
"nix-gaming": "nix-gaming",
"nix-index-database": "nix-index-database",
"nixos-hardware": "nixos-hardware",
"nixos-mailserver": "nixos-mailserver",
"nixos-router": "nixos-router",
"nixpkgs": "nixpkgs_3",
"nixpkgs": "nixpkgs",
"nixpkgs-kernel": "nixpkgs-kernel",
"notlua": "notlua",
"notnft": "notnft",
"nur": "nur",
"osu-wine": "osu-wine",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"flake-utils": "flake-utils_2",
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
@ -782,52 +434,6 @@
"type": "github"
}
},
"sops-nix": {
"inputs": {
"nixpkgs": [
"nix-community-infra",
"nixpkgs"
],
"nixpkgs-stable": [
"nix-community-infra",
"empty"
]
},
"locked": {
"lastModified": 1719716556,
"narHash": "sha256-KA9gy2Wkv76s4A8eLnOcdKVTygewbw3xsB8+awNMyqs=",
"owner": "Mic92",
"repo": "sops-nix",
"rev": "b5974d4331fb6c893e808977a2e1a6d34b3162d6",
"type": "github"
},
"original": {
"owner": "Mic92",
"repo": "sops-nix",
"type": "github"
}
},
"srvos": {
"inputs": {
"nixpkgs": [
"nix-community-infra",
"nixpkgs"
]
},
"locked": {
"lastModified": 1719835186,
"narHash": "sha256-o0FB8SQVLOnbsYTk2Bt6gXwsfqEv4ZHsGP50/kM/gR0=",
"owner": "nix-community",
"repo": "srvos",
"rev": "14b3b0aa48fa291f1be26ab8948d5b9eadaed0b8",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "srvos",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
@ -858,60 +464,9 @@
"type": "github"
}
},
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"systems_4": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": {
"inputs": {
"nixpkgs": [
"nix-community-infra",
"nixpkgs"
]
},
"locked": {
"lastModified": 1719749022,
"narHash": "sha256-ddPKHcqaKCIFSFc/cvxS14goUhCOAwsM1PbMr0ZtHMg=",
"owner": "numtide",
"repo": "treefmt-nix",
"rev": "8df5ff62195d4e67e2264df0b7f5e8c9995fd0bd",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "treefmt-nix",
"type": "github"
}
},
"utils": {
"inputs": {
"systems": "systems_2"
"systems": "systems"
},
"locked": {
"lastModified": 1709126324,

View file

@ -4,7 +4,6 @@
inputs = {
#nixpkgs.url = "github:NixOS/nixpkgs/3dc2b4f8166f744c3b3e9ff8224e7c5d74a5424f";
# nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
nix-community-infra.url = "github:nix-community/infra";
nixpkgs-kernel.url = "github:NixOS/nixpkgs/nixos-unstable";
nixpkgs.url = "github:chayleaf/nixpkgs";
nixos-hardware.url = "github:NixOS/nixos-hardware";
@ -17,10 +16,6 @@
url = "github:chayleaf/mobile-nixos/fix-op6-modem";
flake = false;
};
osu-wine = {
url = "github:chayleaf/osu-wine.nix";
inputs.nixpkgs.follows = "nixpkgs";
};
impermanence.url = "github:nix-community/impermanence";
nur.url = "github:nix-community/NUR";
rust-overlay = {

View file

@ -19,7 +19,6 @@ in
{
inherit (inputs.nix-gaming.packages.${pkgs.system}) faf-client osu-lazer-bin;
inherit (inputs.osu-wine.packages.${pkgs.system}) osu-wine;
matrix-appservice-discord = pkgs.callPackage ./matrix-appservice-discord { inherit (pkgs) matrix-appservice-discord; };
openssh = pkgs.openssh.overrideAttrs (old: rec {
@ -55,6 +54,7 @@ in
patches = [ ./looking-glass.patch ];
});
mobile-config-firefox = callPackage ./mobile-config-firefox { };
osu-wine = callPackage ./osu-wine { };
ping-exporter = callPackage ./ping-exporter { };
proton-ge = pkgs.stdenvNoCC.mkDerivation {
inherit (sources.proton-ge) pname version src;

View file

@ -0,0 +1,9 @@
MODULE = mmdevapi.dll
IMPORTS = uuid ole32 oleaut32 user32 advapi32
SOURCES = \
audiovolume.c \
devenum.c \
main.c \
mmdevapi_classes.idl \
spatialaudio.c

View file

@ -0,0 +1,320 @@
/*
* Copyright 2010 Maarten Lankhorst 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 COBJMACROS
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "winreg.h"
#include "wine/debug.h"
#include "ole2.h"
#include "mmdeviceapi.h"
#include "mmsystem.h"
#include "dsound.h"
#include "audioclient.h"
#include "endpointvolume.h"
#include "audiopolicy.h"
#include "spatialaudioclient.h"
#include "mmdevapi.h"
WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
typedef struct AEVImpl {
IAudioEndpointVolumeEx IAudioEndpointVolumeEx_iface;
LONG ref;
float master_vol;
BOOL mute;
} AEVImpl;
static inline AEVImpl *impl_from_IAudioEndpointVolumeEx(IAudioEndpointVolumeEx *iface)
{
return CONTAINING_RECORD(iface, AEVImpl, IAudioEndpointVolumeEx_iface);
}
static void AudioEndpointVolume_Destroy(AEVImpl *This)
{
HeapFree(GetProcessHeap(), 0, This);
}
static HRESULT WINAPI AEV_QueryInterface(IAudioEndpointVolumeEx *iface, REFIID riid, void **ppv)
{
AEVImpl *This = impl_from_IAudioEndpointVolumeEx(iface);
TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IAudioEndpointVolume) ||
IsEqualIID(riid, &IID_IAudioEndpointVolumeEx)) {
*ppv = &This->IAudioEndpointVolumeEx_iface;
}
else
return E_NOINTERFACE;
IUnknown_AddRef((IUnknown *)*ppv);
return S_OK;
}
static ULONG WINAPI AEV_AddRef(IAudioEndpointVolumeEx *iface)
{
AEVImpl *This = impl_from_IAudioEndpointVolumeEx(iface);
ULONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p) new ref %lu\n", This, ref);
return ref;
}
static ULONG WINAPI AEV_Release(IAudioEndpointVolumeEx *iface)
{
AEVImpl *This = impl_from_IAudioEndpointVolumeEx(iface);
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p) new ref %lu\n", This, ref);
if (!ref)
AudioEndpointVolume_Destroy(This);
return ref;
}
static HRESULT WINAPI AEV_RegisterControlChangeNotify(IAudioEndpointVolumeEx *iface, IAudioEndpointVolumeCallback *notify)
{
TRACE("(%p)->(%p)\n", iface, notify);
if (!notify)
return E_POINTER;
FIXME("stub\n");
return S_OK;
}
static HRESULT WINAPI AEV_UnregisterControlChangeNotify(IAudioEndpointVolumeEx *iface, IAudioEndpointVolumeCallback *notify)
{
TRACE("(%p)->(%p)\n", iface, notify);
if (!notify)
return E_POINTER;
FIXME("stub\n");
return S_OK;
}
static HRESULT WINAPI AEV_GetChannelCount(IAudioEndpointVolumeEx *iface, UINT *count)
{
TRACE("(%p)->(%p)\n", iface, count);
if (!count)
return E_POINTER;
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI AEV_SetMasterVolumeLevel(IAudioEndpointVolumeEx *iface, float leveldb, const GUID *ctx)
{
AEVImpl *This = impl_from_IAudioEndpointVolumeEx(iface);
TRACE("(%p)->(%f,%s)\n", iface, leveldb, debugstr_guid(ctx));
if(leveldb < -100.f || leveldb > 0.f)
return E_INVALIDARG;
This->master_vol = leveldb;
return S_OK;
}
static HRESULT WINAPI AEV_SetMasterVolumeLevelScalar(IAudioEndpointVolumeEx *iface, float level, const GUID *ctx)
{
TRACE("(%p)->(%f,%s)\n", iface, level, debugstr_guid(ctx));
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI AEV_GetMasterVolumeLevel(IAudioEndpointVolumeEx *iface, float *leveldb)
{
AEVImpl *This = impl_from_IAudioEndpointVolumeEx(iface);
TRACE("(%p)->(%p)\n", iface, leveldb);
if (!leveldb)
return E_POINTER;
*leveldb = This->master_vol;
return S_OK;
}
static HRESULT WINAPI AEV_GetMasterVolumeLevelScalar(IAudioEndpointVolumeEx *iface, float *level)
{
TRACE("(%p)->(%p)\n", iface, level);
if (!level)
return E_POINTER;
FIXME("stub\n");
*level = 1.0;
return S_OK;
}
static HRESULT WINAPI AEV_SetChannelVolumeLevel(IAudioEndpointVolumeEx *iface, UINT chan, float leveldb, const GUID *ctx)
{
TRACE("(%p)->(%f,%s)\n", iface, leveldb, debugstr_guid(ctx));
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI AEV_SetChannelVolumeLevelScalar(IAudioEndpointVolumeEx *iface, UINT chan, float level, const GUID *ctx)
{
TRACE("(%p)->(%u,%f,%s)\n", iface, chan, level, debugstr_guid(ctx));
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI AEV_GetChannelVolumeLevel(IAudioEndpointVolumeEx *iface, UINT chan, float *leveldb)
{
TRACE("(%p)->(%u,%p)\n", iface, chan, leveldb);
if (!leveldb)
return E_POINTER;
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI AEV_GetChannelVolumeLevelScalar(IAudioEndpointVolumeEx *iface, UINT chan, float *level)
{
TRACE("(%p)->(%u,%p)\n", iface, chan, level);
if (!level)
return E_POINTER;
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI AEV_SetMute(IAudioEndpointVolumeEx *iface, BOOL mute, const GUID *ctx)
{
AEVImpl *This = impl_from_IAudioEndpointVolumeEx(iface);
HRESULT ret;
TRACE("(%p)->(%u,%s)\n", iface, mute, debugstr_guid(ctx));
ret = This->mute == mute ? S_FALSE : S_OK;
This->mute = mute;
return ret;
}
static HRESULT WINAPI AEV_GetMute(IAudioEndpointVolumeEx *iface, BOOL *mute)
{
AEVImpl *This = impl_from_IAudioEndpointVolumeEx(iface);
TRACE("(%p)->(%p)\n", iface, mute);
if (!mute)
return E_POINTER;
*mute = This->mute;
return S_OK;
}
static HRESULT WINAPI AEV_GetVolumeStepInfo(IAudioEndpointVolumeEx *iface, UINT *stepsize, UINT *stepcount)
{
TRACE("(%p)->(%p,%p)\n", iface, stepsize, stepcount);
if (!stepsize && !stepcount)
return E_POINTER;
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI AEV_VolumeStepUp(IAudioEndpointVolumeEx *iface, const GUID *ctx)
{
TRACE("(%p)->(%s)\n", iface, debugstr_guid(ctx));
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI AEV_VolumeStepDown(IAudioEndpointVolumeEx *iface, const GUID *ctx)
{
TRACE("(%p)->(%s)\n", iface, debugstr_guid(ctx));
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI AEV_QueryHardwareSupport(IAudioEndpointVolumeEx *iface, DWORD *mask)
{
TRACE("(%p)->(%p)\n", iface, mask);
if (!mask)
return E_POINTER;
FIXME("stub\n");
return E_NOTIMPL;
}
static HRESULT WINAPI AEV_GetVolumeRange(IAudioEndpointVolumeEx *iface, float *mindb, float *maxdb, float *inc)
{
TRACE("(%p)->(%p,%p,%p)\n", iface, mindb, maxdb, inc);
if (!mindb || !maxdb || !inc)
return E_POINTER;
*mindb = -100.f;
*maxdb = 0.f;
*inc = 1.f;
return S_OK;
}
static HRESULT WINAPI AEV_GetVolumeRangeChannel(IAudioEndpointVolumeEx *iface, UINT chan, float *mindb, float *maxdb, float *inc)
{
TRACE("(%p)->(%p,%p,%p)\n", iface, mindb, maxdb, inc);
if (!mindb || !maxdb || !inc)
return E_POINTER;
FIXME("stub\n");
return E_NOTIMPL;
}
static const IAudioEndpointVolumeExVtbl AEVImpl_Vtbl = {
AEV_QueryInterface,
AEV_AddRef,
AEV_Release,
AEV_RegisterControlChangeNotify,
AEV_UnregisterControlChangeNotify,
AEV_GetChannelCount,
AEV_SetMasterVolumeLevel,
AEV_SetMasterVolumeLevelScalar,
AEV_GetMasterVolumeLevel,
AEV_GetMasterVolumeLevelScalar,
AEV_SetChannelVolumeLevel,
AEV_SetChannelVolumeLevelScalar,
AEV_GetChannelVolumeLevel,
AEV_GetChannelVolumeLevelScalar,
AEV_SetMute,
AEV_GetMute,
AEV_GetVolumeStepInfo,
AEV_VolumeStepUp,
AEV_VolumeStepDown,
AEV_QueryHardwareSupport,
AEV_GetVolumeRange,
AEV_GetVolumeRangeChannel
};
HRESULT AudioEndpointVolume_Create(MMDevice *parent, IAudioEndpointVolumeEx **ppv)
{
AEVImpl *This;
*ppv = NULL;
This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
if (!This)
return E_OUTOFMEMORY;
This->IAudioEndpointVolumeEx_iface.lpVtbl = &AEVImpl_Vtbl;
This->ref = 1;
*ppv = &This->IAudioEndpointVolumeEx_iface;
return S_OK;
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,470 @@
/*
* Copyright 2009 Maarten Lankhorst
* Copyright 2011 Andrew Eikum for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#define COBJMACROS
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "ole2.h"
#include "olectl.h"
#include "rpcproxy.h"
#include "propsys.h"
#include "propkeydef.h"
#include "mmdeviceapi.h"
#include "mmsystem.h"
#include "dsound.h"
#include "audioclient.h"
#include "endpointvolume.h"
#include "audiopolicy.h"
#include "devpkey.h"
#include "winreg.h"
#include "spatialaudioclient.h"
#include "mmdevapi.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
DriverFuncs drvs;
const WCHAR drv_keyW[] = L"Software\\Wine\\Drivers";
static const char *get_priority_string(int prio)
{
switch(prio){
case Priority_Unavailable:
return "Unavailable";
case Priority_Low:
return "Low";
case Priority_Neutral:
return "Neutral";
case Priority_Preferred:
return "Preferred";
}
return "Invalid";
}
static BOOL load_driver(const WCHAR *name, DriverFuncs *driver)
{
WCHAR driver_module[264];
lstrcpyW(driver_module, L"wine");
lstrcatW(driver_module, name);
lstrcatW(driver_module, L".drv");
TRACE("Attempting to load %s\n", wine_dbgstr_w(driver_module));
driver->module = LoadLibraryW(driver_module);
if(!driver->module){
TRACE("Unable to load %s: %lu\n", wine_dbgstr_w(driver_module),
GetLastError());
return FALSE;
}
#define LDFC(n) do { driver->p##n = (void*)GetProcAddress(driver->module, #n);\
if(!driver->p##n) { FreeLibrary(driver->module); return FALSE; } } while(0)
LDFC(GetPriority);
LDFC(GetEndpointIDs);
LDFC(GetAudioEndpoint);
LDFC(GetAudioSessionManager);
#undef LDFC
/* optional - do not fail if not found */
driver->pGetPropValue = (void*)GetProcAddress(driver->module, "GetPropValue");
driver->priority = driver->pGetPriority();
lstrcpyW(driver->module_name, driver_module);
TRACE("Successfully loaded %s with priority %s\n",
wine_dbgstr_w(driver_module), get_priority_string(driver->priority));
return TRUE;
}
static BOOL WINAPI init_driver(INIT_ONCE *once, void *param, void **context)
{
static WCHAR default_list[] = L"pulse,alsa,oss,coreaudio";
DriverFuncs driver;
HKEY key;
WCHAR reg_list[256], *p, *next, *driver_list = default_list;
if(RegOpenKeyW(HKEY_CURRENT_USER, drv_keyW, &key) == ERROR_SUCCESS){
DWORD size = sizeof(reg_list);
if(RegQueryValueExW(key, L"Audio", 0, NULL, (BYTE*)reg_list, &size) == ERROR_SUCCESS){
if(reg_list[0] == '\0'){
TRACE("User explicitly chose no driver\n");
RegCloseKey(key);
return TRUE;
}
driver_list = reg_list;
}
RegCloseKey(key);
}
TRACE("Loading driver list %s\n", wine_dbgstr_w(driver_list));
for(next = p = driver_list; next; p = next + 1){
next = wcschr(p, ',');
if(next)
*next = '\0';
driver.priority = Priority_Unavailable;
if(load_driver(p, &driver)){
if(driver.priority == Priority_Unavailable)
FreeLibrary(driver.module);
else if(!drvs.module || driver.priority > drvs.priority){
TRACE("Selecting driver %s with priority %s\n",
wine_dbgstr_w(p), get_priority_string(driver.priority));
if(drvs.module)
FreeLibrary(drvs.module);
drvs = driver;
}else
FreeLibrary(driver.module);
}else
TRACE("Failed to load driver %s\n", wine_dbgstr_w(p));
if(next)
*next = ',';
}
if (drvs.module != 0){
load_devices_from_reg();
load_driver_devices(eRender);
load_driver_devices(eCapture);
}
return drvs.module != 0;
}
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
TRACE("(0x%p, %ld, %p)\n", hinstDLL, fdwReason, lpvReserved);
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls(hinstDLL);
break;
case DLL_PROCESS_DETACH:
if(lpvReserved)
break;
MMDevEnum_Free();
break;
}
return TRUE;
}
typedef HRESULT (*FnCreateInstance)(REFIID riid, LPVOID *ppobj);
typedef struct {
IClassFactory IClassFactory_iface;
REFCLSID rclsid;
FnCreateInstance pfnCreateInstance;
} IClassFactoryImpl;
static inline IClassFactoryImpl *impl_from_IClassFactory(IClassFactory *iface)
{
return CONTAINING_RECORD(iface, IClassFactoryImpl, IClassFactory_iface);
}
static HRESULT WINAPI
MMCF_QueryInterface(IClassFactory *iface, REFIID riid, void **ppobj)
{
IClassFactoryImpl *This = impl_from_IClassFactory(iface);
TRACE("(%p, %s, %p)\n", This, debugstr_guid(riid), ppobj);
if (ppobj == NULL)
return E_POINTER;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IClassFactory))
{
*ppobj = iface;
IClassFactory_AddRef(iface);
return S_OK;
}
*ppobj = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI MMCF_AddRef(LPCLASSFACTORY iface)
{
return 2;
}
static ULONG WINAPI MMCF_Release(LPCLASSFACTORY iface)
{
/* static class, won't be freed */
return 1;
}
static HRESULT WINAPI MMCF_CreateInstance(
LPCLASSFACTORY iface,
LPUNKNOWN pOuter,
REFIID riid,
LPVOID *ppobj)
{
IClassFactoryImpl *This = impl_from_IClassFactory(iface);
TRACE("(%p, %p, %s, %p)\n", This, pOuter, debugstr_guid(riid), ppobj);
if (pOuter)
return CLASS_E_NOAGGREGATION;
if (ppobj == NULL) {
WARN("invalid parameter\n");
return E_POINTER;
}
*ppobj = NULL;
return This->pfnCreateInstance(riid, ppobj);
}
static HRESULT WINAPI MMCF_LockServer(LPCLASSFACTORY iface, BOOL dolock)
{
IClassFactoryImpl *This = impl_from_IClassFactory(iface);
FIXME("(%p, %d) stub!\n", This, dolock);
return S_OK;
}
static const IClassFactoryVtbl MMCF_Vtbl = {
MMCF_QueryInterface,
MMCF_AddRef,
MMCF_Release,
MMCF_CreateInstance,
MMCF_LockServer
};
static IClassFactoryImpl MMDEVAPI_CF[] = {
{ { &MMCF_Vtbl }, &CLSID_MMDeviceEnumerator, (FnCreateInstance)MMDevEnum_Create }
};
HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
unsigned int i = 0;
TRACE("(%s, %s, %p)\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv);
if(!InitOnceExecuteOnce(&init_once, init_driver, NULL, NULL)) {
ERR("Driver initialization failed\n");
return E_FAIL;
}
if (ppv == NULL) {
WARN("invalid parameter\n");
return E_INVALIDARG;
}
*ppv = NULL;
if (!IsEqualIID(riid, &IID_IClassFactory) &&
!IsEqualIID(riid, &IID_IUnknown)) {
WARN("no interface for %s\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
for (i = 0; i < ARRAY_SIZE(MMDEVAPI_CF); ++i)
{
if (IsEqualGUID(rclsid, MMDEVAPI_CF[i].rclsid)) {
IClassFactory_AddRef(&MMDEVAPI_CF[i].IClassFactory_iface);
*ppv = &MMDEVAPI_CF[i];
return S_OK;
}
}
WARN("(%s, %s, %p): no class found.\n", debugstr_guid(rclsid),
debugstr_guid(riid), ppv);
return CLASS_E_CLASSNOTAVAILABLE;
}
struct activate_async_op {
IActivateAudioInterfaceAsyncOperation IActivateAudioInterfaceAsyncOperation_iface;
LONG ref;
IActivateAudioInterfaceCompletionHandler *callback;
HRESULT result_hr;
IUnknown *result_iface;
};
static struct activate_async_op *impl_from_IActivateAudioInterfaceAsyncOperation(IActivateAudioInterfaceAsyncOperation *iface)
{
return CONTAINING_RECORD(iface, struct activate_async_op, IActivateAudioInterfaceAsyncOperation_iface);
}
static HRESULT WINAPI activate_async_op_QueryInterface(IActivateAudioInterfaceAsyncOperation *iface,
REFIID riid, void **ppv)
{
struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface);
TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IActivateAudioInterfaceAsyncOperation)) {
*ppv = &This->IActivateAudioInterfaceAsyncOperation_iface;
} else {
*ppv = NULL;
return E_NOINTERFACE;
}
IUnknown_AddRef((IUnknown*)*ppv);
return S_OK;
}
static ULONG WINAPI activate_async_op_AddRef(IActivateAudioInterfaceAsyncOperation *iface)
{
struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface);
LONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p) refcount now %li\n", This, ref);
return ref;
}
static ULONG WINAPI activate_async_op_Release(IActivateAudioInterfaceAsyncOperation *iface)
{
struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface);
LONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p) refcount now %li\n", This, ref);
if (!ref) {
if(This->result_iface)
IUnknown_Release(This->result_iface);
IActivateAudioInterfaceCompletionHandler_Release(This->callback);
HeapFree(GetProcessHeap(), 0, This);
}
return ref;
}
static HRESULT WINAPI activate_async_op_GetActivateResult(IActivateAudioInterfaceAsyncOperation *iface,
HRESULT *result_hr, IUnknown **result_iface)
{
struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface);
TRACE("(%p)->(%p, %p)\n", This, result_hr, result_iface);
*result_hr = This->result_hr;
if(This->result_hr == S_OK){
*result_iface = This->result_iface;
IUnknown_AddRef(*result_iface);
}
return S_OK;
}
static IActivateAudioInterfaceAsyncOperationVtbl IActivateAudioInterfaceAsyncOperation_vtbl = {
activate_async_op_QueryInterface,
activate_async_op_AddRef,
activate_async_op_Release,
activate_async_op_GetActivateResult,
};
static DWORD WINAPI activate_async_threadproc(void *user)
{
struct activate_async_op *op = user;
SetThreadDescription(GetCurrentThread(), L"wine_mmdevapi_activate_async");
IActivateAudioInterfaceCompletionHandler_ActivateCompleted(op->callback, &op->IActivateAudioInterfaceAsyncOperation_iface);
IActivateAudioInterfaceAsyncOperation_Release(&op->IActivateAudioInterfaceAsyncOperation_iface);
return 0;
}
static HRESULT get_mmdevice_by_activatepath(const WCHAR *path, IMMDevice **mmdev)
{
IMMDeviceEnumerator *devenum;
HRESULT hr;
static const WCHAR DEVINTERFACE_AUDIO_RENDER_WSTR[] = L"{E6327CAD-DCEC-4949-AE8A-991E976A79D2}";
static const WCHAR DEVINTERFACE_AUDIO_CAPTURE_WSTR[] = L"{2EEF81BE-33FA-4800-9670-1CD474972C3F}";
static const WCHAR MMDEV_PATH_PREFIX[] = L"\\\\?\\SWD#MMDEVAPI#";
hr = MMDevEnum_Create(&IID_IMMDeviceEnumerator, (void**)&devenum);
if (FAILED(hr)) {
WARN("Failed to create MMDeviceEnumerator: %08lx\n", hr);
return hr;
}
if (!lstrcmpiW(path, DEVINTERFACE_AUDIO_RENDER_WSTR)){
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, eRender, eMultimedia, mmdev);
} else if (!lstrcmpiW(path, DEVINTERFACE_AUDIO_CAPTURE_WSTR)){
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, eCapture, eMultimedia, mmdev);
} else if (!memcmp(path, MMDEV_PATH_PREFIX, sizeof(MMDEV_PATH_PREFIX) - sizeof(WCHAR))) {
WCHAR device_id[56]; /* == strlen("{0.0.1.00000000}.{fd47d9cc-4218-4135-9ce2-0c195c87405b}") + 1 */
lstrcpynW(device_id, path + (ARRAY_SIZE(MMDEV_PATH_PREFIX) - 1), ARRAY_SIZE(device_id));
hr = IMMDeviceEnumerator_GetDevice(devenum, device_id, mmdev);
} else {
FIXME("Unrecognized device id format: %s\n", debugstr_w(path));
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
if (FAILED(hr)) {
WARN("Failed to get requested device (%s): %08lx\n", debugstr_w(path), hr);
*mmdev = NULL;
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
IMMDeviceEnumerator_Release(devenum);
return hr;
}
/***********************************************************************
* ActivateAudioInterfaceAsync (MMDEVAPI.17)
*/
HRESULT WINAPI ActivateAudioInterfaceAsync(const WCHAR *path, REFIID riid,
PROPVARIANT *params, IActivateAudioInterfaceCompletionHandler *done_handler,
IActivateAudioInterfaceAsyncOperation **op_out)
{
struct activate_async_op *op;
HANDLE ht;
IMMDevice *mmdev;
TRACE("(%s, %s, %p, %p, %p)\n", debugstr_w(path), debugstr_guid(riid),
params, done_handler, op_out);
op = HeapAlloc(GetProcessHeap(), 0, sizeof(*op));
if (!op)
return E_OUTOFMEMORY;
op->ref = 2; /* returned ref and threadproc ref */
op->IActivateAudioInterfaceAsyncOperation_iface.lpVtbl = &IActivateAudioInterfaceAsyncOperation_vtbl;
op->callback = done_handler;
IActivateAudioInterfaceCompletionHandler_AddRef(done_handler);
op->result_hr = get_mmdevice_by_activatepath(path, &mmdev);
if (SUCCEEDED(op->result_hr)) {
op->result_hr = IMMDevice_Activate(mmdev, riid, CLSCTX_INPROC_SERVER, params, (void**)&op->result_iface);
IMMDevice_Release(mmdev);
}else
op->result_iface = NULL;
ht = CreateThread(NULL, 0, &activate_async_threadproc, op, 0, NULL);
CloseHandle(ht);
*op_out = &op->IActivateAudioInterfaceAsyncOperation_iface;
return S_OK;
}

View file

@ -0,0 +1,76 @@
/*
* Copyright 2009 Maarten Lankhorst
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "unixlib.h"
#include <wine/unixlib.h>
#include <wine/list.h>
extern HRESULT MMDevEnum_Create(REFIID riid, void **ppv);
extern void MMDevEnum_Free(void);
typedef struct _DriverFuncs {
HMODULE module;
WCHAR module_name[64];
int priority;
/* Returns a "priority" value for the driver. Highest priority wins.
* If multiple drivers think they are valid, they will return a
* priority value reflecting the likelihood that they are actually
* valid. See enum _DriverPriority. */
int (WINAPI *pGetPriority)(void);
/* ids gets an array of human-friendly endpoint names
* keys gets an array of driver-specific stuff that is used
* in GetAudioEndpoint to identify the endpoint
* it is the caller's responsibility to free both arrays, and
* all of the elements in both arrays with HeapFree() */
HRESULT (WINAPI *pGetEndpointIDs)(EDataFlow flow, WCHAR ***ids,
GUID **guids, UINT *num, UINT *default_index);
HRESULT (WINAPI *pGetAudioEndpoint)(void *key, IMMDevice *dev,
IAudioClient **out);
HRESULT (WINAPI *pGetAudioSessionManager)(IMMDevice *device,
IAudioSessionManager2 **out);
HRESULT (WINAPI *pGetPropValue)(GUID *guid,
const PROPERTYKEY *prop, PROPVARIANT *out);
} DriverFuncs;
extern DriverFuncs drvs;
typedef struct MMDevice {
IMMDevice IMMDevice_iface;
IMMEndpoint IMMEndpoint_iface;
LONG ref;
CRITICAL_SECTION crst;
EDataFlow flow;
DWORD state;
GUID devguid;
WCHAR *drv_id;
struct list entry;
} MMDevice;
extern HRESULT AudioClient_Create(MMDevice *parent, IAudioClient **ppv);
extern HRESULT AudioEndpointVolume_Create(MMDevice *parent, IAudioEndpointVolumeEx **ppv);
extern HRESULT SpatialAudioClient_Create(IMMDevice *device, ISpatialAudioClient **out);
extern HRESULT load_devices_from_reg(void);
extern HRESULT load_driver_devices(EDataFlow flow);
extern const WCHAR drv_keyW[];

View file

@ -0,0 +1,21 @@
2 stub @
3 stub @
4 stub @
5 stub @
6 stub @
7 stub @
8 stub @
9 stub @
10 stub @
11 stub @
12 stub @
13 stub @
14 stub @
15 stub @
16 stub @
17 stdcall ActivateAudioInterfaceAsync( wstr ptr ptr ptr ptr )
@ stdcall -private DllCanUnloadNow()
@ stdcall -private DllGetClassObject( ptr ptr ptr )
@ stdcall -private DllRegisterServer()
@ stdcall -private DllUnregisterServer()

View file

@ -0,0 +1,28 @@
/*
* COM Classes for mmdevapi
*
* Copyright 2010 Alexandre Julliard
*
* 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 register
[
helpstring("MMDeviceEnumerator class"),
threading(both),
uuid(bcde0395-e52f-467c-8e3d-c4579291692e)
]
coclass MMDeviceEnumerator { interface IMMDeviceEnumerator; }

View file

@ -0,0 +1,976 @@
/*
* Copyright 2020 Andrew Eikum for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#define COBJMACROS
#define NONAMELESSUNION
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "winnls.h"
#include "winreg.h"
#include "wine/heap.h"
#include "wine/debug.h"
#include "wine/list.h"
#include "ole2.h"
#include "mmdeviceapi.h"
#include "mmsystem.h"
#include "audioclient.h"
#include "endpointvolume.h"
#include "audiopolicy.h"
#include "spatialaudioclient.h"
#include "mmdevapi.h"
WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
static UINT32 AudioObjectType_to_index(AudioObjectType type)
{
UINT32 o = 0;
while(type){
type >>= 1;
++o;
}
return o - 2;
}
typedef struct SpatialAudioImpl SpatialAudioImpl;
typedef struct SpatialAudioStreamImpl SpatialAudioStreamImpl;
typedef struct SpatialAudioObjectImpl SpatialAudioObjectImpl;
struct SpatialAudioObjectImpl {
ISpatialAudioObject ISpatialAudioObject_iface;
LONG ref;
SpatialAudioStreamImpl *sa_stream;
AudioObjectType type;
UINT32 static_idx;
float *buf;
struct list entry;
};
struct SpatialAudioStreamImpl {
ISpatialAudioObjectRenderStream ISpatialAudioObjectRenderStream_iface;
LONG ref;
CRITICAL_SECTION lock;
SpatialAudioImpl *sa_client;
SpatialAudioObjectRenderStreamActivationParams params;
IAudioClient *client;
IAudioRenderClient *render;
UINT32 period_frames, update_frames;
WAVEFORMATEXTENSIBLE stream_fmtex;
float *buf;
UINT32 static_object_map[17];
struct list objects;
};
struct SpatialAudioImpl {
ISpatialAudioClient ISpatialAudioClient_iface;
IAudioFormatEnumerator IAudioFormatEnumerator_iface;
IMMDevice *mmdev;
LONG ref;
WAVEFORMATEXTENSIBLE object_fmtex;
};
static inline SpatialAudioObjectImpl *impl_from_ISpatialAudioObject(ISpatialAudioObject *iface)
{
return CONTAINING_RECORD(iface, SpatialAudioObjectImpl, ISpatialAudioObject_iface);
}
static inline SpatialAudioStreamImpl *impl_from_ISpatialAudioObjectRenderStream(ISpatialAudioObjectRenderStream *iface)
{
return CONTAINING_RECORD(iface, SpatialAudioStreamImpl, ISpatialAudioObjectRenderStream_iface);
}
static inline SpatialAudioImpl *impl_from_ISpatialAudioClient(ISpatialAudioClient *iface)
{
return CONTAINING_RECORD(iface, SpatialAudioImpl, ISpatialAudioClient_iface);
}
static inline SpatialAudioImpl *impl_from_IAudioFormatEnumerator(IAudioFormatEnumerator *iface)
{
return CONTAINING_RECORD(iface, SpatialAudioImpl, IAudioFormatEnumerator_iface);
}
static HRESULT WINAPI SAO_QueryInterface(ISpatialAudioObject *iface,
REFIID riid, void **ppv)
{
SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_ISpatialAudioObjectBase) ||
IsEqualIID(riid, &IID_ISpatialAudioObject)) {
*ppv = &This->ISpatialAudioObject_iface;
}
else
return E_NOINTERFACE;
IUnknown_AddRef((IUnknown *)*ppv);
return S_OK;
}
static ULONG WINAPI SAO_AddRef(ISpatialAudioObject *iface)
{
SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
ULONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p) new ref %lu\n", This, ref);
return ref;
}
static ULONG WINAPI SAO_Release(ISpatialAudioObject *iface)
{
SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p) new ref %lu\n", This, ref);
if(!ref){
EnterCriticalSection(&This->sa_stream->lock);
list_remove(&This->entry);
LeaveCriticalSection(&This->sa_stream->lock);
ISpatialAudioObjectRenderStream_Release(&This->sa_stream->ISpatialAudioObjectRenderStream_iface);
heap_free(This->buf);
heap_free(This);
}
return ref;
}
static HRESULT WINAPI SAO_GetBuffer(ISpatialAudioObject *iface,
BYTE **buffer, UINT32 *bytes)
{
SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
TRACE("(%p)->(%p, %p)\n", This, buffer, bytes);
EnterCriticalSection(&This->sa_stream->lock);
if(This->sa_stream->update_frames == ~0){
LeaveCriticalSection(&This->sa_stream->lock);
return SPTLAUDCLNT_E_OUT_OF_ORDER;
}
*buffer = (BYTE *)This->buf;
*bytes = This->sa_stream->update_frames *
This->sa_stream->sa_client->object_fmtex.Format.nBlockAlign;
LeaveCriticalSection(&This->sa_stream->lock);
return S_OK;
}
static HRESULT WINAPI SAO_SetEndOfStream(ISpatialAudioObject *iface, UINT32 frames)
{
SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
FIXME("(%p)->(%u)\n", This, frames);
return E_NOTIMPL;
}
static HRESULT WINAPI SAO_IsActive(ISpatialAudioObject *iface, BOOL *active)
{
SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
FIXME("(%p)->(%p)\n", This, active);
return E_NOTIMPL;
}
static HRESULT WINAPI SAO_GetAudioObjectType(ISpatialAudioObject *iface,
AudioObjectType *type)
{
SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
TRACE("(%p)->(%p)\n", This, type);
*type = This->type;
return S_OK;
}
static HRESULT WINAPI SAO_SetPosition(ISpatialAudioObject *iface, float x,
float y, float z)
{
SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
FIXME("(%p)->(%f, %f, %f)\n", This, x, y, z);
return E_NOTIMPL;
}
static HRESULT WINAPI SAO_SetVolume(ISpatialAudioObject *iface, float vol)
{
SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
FIXME("(%p)->(%f)\n", This, vol);
return E_NOTIMPL;
}
static ISpatialAudioObjectVtbl ISpatialAudioObject_vtbl = {
SAO_QueryInterface,
SAO_AddRef,
SAO_Release,
SAO_GetBuffer,
SAO_SetEndOfStream,
SAO_IsActive,
SAO_GetAudioObjectType,
SAO_SetPosition,
SAO_SetVolume,
};
static HRESULT WINAPI SAORS_QueryInterface(ISpatialAudioObjectRenderStream *iface,
REFIID riid, void **ppv)
{
SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStreamBase) ||
IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStream)) {
*ppv = &This->ISpatialAudioObjectRenderStream_iface;
}
else
return E_NOINTERFACE;
IUnknown_AddRef((IUnknown *)*ppv);
return S_OK;
}
static ULONG WINAPI SAORS_AddRef(ISpatialAudioObjectRenderStream *iface)
{
SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
ULONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p) new ref %lu\n", This, ref);
return ref;
}
static ULONG WINAPI SAORS_Release(ISpatialAudioObjectRenderStream *iface)
{
SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p) new ref %lu\n", This, ref);
if(!ref){
IAudioClient_Stop(This->client);
if(This->update_frames != ~0 && This->update_frames > 0)
IAudioRenderClient_ReleaseBuffer(This->render, This->update_frames, 0);
IAudioRenderClient_Release(This->render);
IAudioClient_Release(This->client);
if(This->params.NotifyObject)
ISpatialAudioObjectRenderStreamNotify_Release(This->params.NotifyObject);
heap_free((void*)This->params.ObjectFormat);
CloseHandle(This->params.EventHandle);
DeleteCriticalSection(&This->lock);
ISpatialAudioClient_Release(&This->sa_client->ISpatialAudioClient_iface);
heap_free(This);
}
return ref;
}
static HRESULT WINAPI SAORS_GetAvailableDynamicObjectCount(
ISpatialAudioObjectRenderStream *iface, UINT32 *count)
{
SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
FIXME("(%p)->(%p)\n", This, count);
*count = 0;
return S_OK;
}
static HRESULT WINAPI SAORS_GetService(ISpatialAudioObjectRenderStream *iface,
REFIID riid, void **service)
{
SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
FIXME("(%p)->(%s, %p)\n", This, debugstr_guid(riid), service);
return E_NOTIMPL;
}
static HRESULT WINAPI SAORS_Start(ISpatialAudioObjectRenderStream *iface)
{
SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
HRESULT hr;
TRACE("(%p)->()\n", This);
hr = IAudioClient_Start(This->client);
if(FAILED(hr)){
WARN("IAudioClient::Start failed: %08lx\n", hr);
return hr;
}
return S_OK;
}
static HRESULT WINAPI SAORS_Stop(ISpatialAudioObjectRenderStream *iface)
{
SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
HRESULT hr;
TRACE("(%p)->()\n", This);
hr = IAudioClient_Stop(This->client);
if(FAILED(hr)){
WARN("IAudioClient::Stop failed: %08lx\n", hr);
return hr;
}
return S_OK;
}
static HRESULT WINAPI SAORS_Reset(ISpatialAudioObjectRenderStream *iface)
{
SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
FIXME("(%p)->()\n", This);
return E_NOTIMPL;
}
static HRESULT WINAPI SAORS_BeginUpdatingAudioObjects(ISpatialAudioObjectRenderStream *iface,
UINT32 *dyn_count, UINT32 *frames)
{
static BOOL fixme_once = FALSE;
SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
SpatialAudioObjectImpl *object;
HRESULT hr;
TRACE("(%p)->(%p, %p)\n", This, dyn_count, frames);
EnterCriticalSection(&This->lock);
if(This->update_frames != ~0){
LeaveCriticalSection(&This->lock);
return SPTLAUDCLNT_E_OUT_OF_ORDER;
}
This->update_frames = This->period_frames;
if(This->update_frames > 0){
hr = IAudioRenderClient_GetBuffer(This->render, This->update_frames, (BYTE **)&This->buf);
if(FAILED(hr)){
WARN("GetBuffer failed: %08lx\n", hr);
This->update_frames = ~0;
LeaveCriticalSection(&This->lock);
return hr;
}
LIST_FOR_EACH_ENTRY(object, &This->objects, SpatialAudioObjectImpl, entry){
memset(object->buf, 0, This->update_frames * This->sa_client->object_fmtex.Format.nBlockAlign);
}
}else if (!fixme_once){
fixme_once = TRUE;
FIXME("Zero frame update.\n");
}
*dyn_count = 0;
*frames = This->update_frames;
LeaveCriticalSection(&This->lock);
return S_OK;
}
static void mix_static_object(SpatialAudioStreamImpl *stream, SpatialAudioObjectImpl *object)
{
float *in = object->buf, *out;
UINT32 i;
if(object->static_idx == ~0 ||
stream->static_object_map[object->static_idx] == ~0){
WARN("Got unmapped static object?! Not mixing. Type: 0x%x\n", object->type);
return;
}
out = stream->buf + stream->static_object_map[object->static_idx];
for(i = 0; i < stream->update_frames; ++i){
*out += *in;
++in;
out += stream->stream_fmtex.Format.nChannels;
}
}
static HRESULT WINAPI SAORS_EndUpdatingAudioObjects(ISpatialAudioObjectRenderStream *iface)
{
SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
SpatialAudioObjectImpl *object;
HRESULT hr;
TRACE("(%p)->()\n", This);
EnterCriticalSection(&This->lock);
if(This->update_frames == ~0){
LeaveCriticalSection(&This->lock);
return SPTLAUDCLNT_E_OUT_OF_ORDER;
}
if(This->update_frames > 0){
LIST_FOR_EACH_ENTRY(object, &This->objects, SpatialAudioObjectImpl, entry){
if(object->type != AudioObjectType_Dynamic)
mix_static_object(This, object);
else
WARN("Don't know how to mix dynamic object yet. %p\n", object);
}
hr = IAudioRenderClient_ReleaseBuffer(This->render, This->update_frames, 0);
if(FAILED(hr))
WARN("ReleaseBuffer failed: %08lx\n", hr);
}
This->update_frames = ~0;
LeaveCriticalSection(&This->lock);
return S_OK;
}
static HRESULT WINAPI SAORS_ActivateSpatialAudioObject(ISpatialAudioObjectRenderStream *iface,
AudioObjectType type, ISpatialAudioObject **object)
{
SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
SpatialAudioObjectImpl *obj;
TRACE("(%p)->(0x%x, %p)\n", This, type, object);
if(type == AudioObjectType_Dynamic)
return SPTLAUDCLNT_E_NO_MORE_OBJECTS;
if(type & ~This->params.StaticObjectTypeMask)
return SPTLAUDCLNT_E_STATIC_OBJECT_NOT_AVAILABLE;
LIST_FOR_EACH_ENTRY(obj, &This->objects, SpatialAudioObjectImpl, entry){
if(obj->static_idx == AudioObjectType_to_index(type))
return SPTLAUDCLNT_E_OBJECT_ALREADY_ACTIVE;
}
obj = heap_alloc_zero(sizeof(*obj));
obj->ISpatialAudioObject_iface.lpVtbl = &ISpatialAudioObject_vtbl;
obj->ref = 1;
obj->type = type;
if(type == AudioObjectType_None){
FIXME("AudioObjectType_None not implemented yet!\n");
obj->static_idx = ~0;
}else{
obj->static_idx = AudioObjectType_to_index(type);
}
obj->sa_stream = This;
SAORS_AddRef(&This->ISpatialAudioObjectRenderStream_iface);
obj->buf = heap_alloc_zero(This->period_frames * This->sa_client->object_fmtex.Format.nBlockAlign);
EnterCriticalSection(&This->lock);
list_add_tail(&This->objects, &obj->entry);
LeaveCriticalSection(&This->lock);
*object = &obj->ISpatialAudioObject_iface;
return S_OK;
}
static ISpatialAudioObjectRenderStreamVtbl ISpatialAudioObjectRenderStream_vtbl = {
SAORS_QueryInterface,
SAORS_AddRef,
SAORS_Release,
SAORS_GetAvailableDynamicObjectCount,
SAORS_GetService,
SAORS_Start,
SAORS_Stop,
SAORS_Reset,
SAORS_BeginUpdatingAudioObjects,
SAORS_EndUpdatingAudioObjects,
SAORS_ActivateSpatialAudioObject,
};
static HRESULT WINAPI SAC_QueryInterface(ISpatialAudioClient *iface, REFIID riid, void **ppv)
{
SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
if (!ppv)
return E_POINTER;
*ppv = NULL;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_ISpatialAudioClient)) {
*ppv = &This->ISpatialAudioClient_iface;
}
else
return E_NOINTERFACE;
IUnknown_AddRef((IUnknown *)*ppv);
return S_OK;
}
static ULONG WINAPI SAC_AddRef(ISpatialAudioClient *iface)
{
SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
ULONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p) new ref %lu\n", This, ref);
return ref;
}
static ULONG WINAPI SAC_Release(ISpatialAudioClient *iface)
{
SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
ULONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p) new ref %lu\n", This, ref);
if (!ref) {
IMMDevice_Release(This->mmdev);
heap_free(This);
}
return ref;
}
static HRESULT WINAPI SAC_GetStaticObjectPosition(ISpatialAudioClient *iface,
AudioObjectType type, float *x, float *y, float *z)
{
SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
FIXME("(%p)->(0x%x, %p, %p, %p)\n", This, type, x, y, z);
return E_NOTIMPL;
}
static HRESULT WINAPI SAC_GetNativeStaticObjectTypeMask(ISpatialAudioClient *iface,
AudioObjectType *mask)
{
SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
FIXME("(%p)->(%p)\n", This, mask);
return E_NOTIMPL;
}
static HRESULT WINAPI SAC_GetMaxDynamicObjectCount(ISpatialAudioClient *iface,
UINT32 *value)
{
SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
FIXME("(%p)->(%p)\n", This, value);
*value = 0;
return S_OK;
}
static HRESULT WINAPI SAC_GetSupportedAudioObjectFormatEnumerator(
ISpatialAudioClient *iface, IAudioFormatEnumerator **enumerator)
{
SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
TRACE("(%p)->(%p)\n", This, enumerator);
*enumerator = &This->IAudioFormatEnumerator_iface;
SAC_AddRef(iface);
return S_OK;
}
static HRESULT WINAPI SAC_GetMaxFrameCount(ISpatialAudioClient *iface,
const WAVEFORMATEX *format, UINT32 *count)
{
SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
/* FIXME: should get device period from the device */
static const REFERENCE_TIME period = 100000;
TRACE("(%p)->(%p, %p)\n", This, format, count);
*count = MulDiv(period, format->nSamplesPerSec, 10000000);
return S_OK;
}
static HRESULT WINAPI SAC_IsAudioObjectFormatSupported(ISpatialAudioClient *iface,
const WAVEFORMATEX *format)
{
SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
FIXME("(%p)->(%p)\n", This, format);
return E_NOTIMPL;
}
static HRESULT WINAPI SAC_IsSpatialAudioStreamAvailable(ISpatialAudioClient *iface,
REFIID stream_uuid, const PROPVARIANT *info)
{
SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
FIXME("(%p)->(%s, %p)\n", This, debugstr_guid(stream_uuid), info);
return E_NOTIMPL;
}
static WAVEFORMATEX *clone_fmtex(const WAVEFORMATEX *src)
{
WAVEFORMATEX *r = heap_alloc(sizeof(WAVEFORMATEX) + src->cbSize);
memcpy(r, src, sizeof(WAVEFORMATEX) + src->cbSize);
return r;
}
static const char *debugstr_fmtex(const WAVEFORMATEX *fmt)
{
static char buf[2048];
if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
snprintf(buf, sizeof(buf), "tag: 0x%x (%s), ch: %u (mask: 0x%lx), rate: %lu, depth: %u",
fmt->wFormatTag, debugstr_guid(&fmtex->SubFormat),
fmt->nChannels, fmtex->dwChannelMask, fmt->nSamplesPerSec,
fmt->wBitsPerSample);
}else{
snprintf(buf, sizeof(buf), "tag: 0x%x, ch: %u, rate: %lu, depth: %u",
fmt->wFormatTag, fmt->nChannels, fmt->nSamplesPerSec,
fmt->wBitsPerSample);
}
return buf;
}
static void static_mask_to_channels(AudioObjectType static_mask, WORD *count, DWORD *mask, UINT32 *map)
{
UINT32 out_chan = 0, map_idx = 0;
*count = 0;
*mask = 0;
#define CONVERT_MASK(f, t) \
if(static_mask & f){ \
*count += 1; \
*mask |= t; \
map[map_idx++] = out_chan++; \
TRACE("mapping 0x%x to %u\n", f, out_chan - 1); \
}else{ \
map[map_idx++] = ~0; \
}
CONVERT_MASK(AudioObjectType_FrontLeft, SPEAKER_FRONT_LEFT);
CONVERT_MASK(AudioObjectType_FrontRight, SPEAKER_FRONT_RIGHT);
CONVERT_MASK(AudioObjectType_FrontCenter, SPEAKER_FRONT_CENTER);
CONVERT_MASK(AudioObjectType_LowFrequency, SPEAKER_LOW_FREQUENCY);
CONVERT_MASK(AudioObjectType_SideLeft, SPEAKER_SIDE_LEFT);
CONVERT_MASK(AudioObjectType_SideRight, SPEAKER_SIDE_RIGHT);
CONVERT_MASK(AudioObjectType_BackLeft, SPEAKER_BACK_LEFT);
CONVERT_MASK(AudioObjectType_BackRight, SPEAKER_BACK_RIGHT);
CONVERT_MASK(AudioObjectType_TopFrontLeft, SPEAKER_TOP_FRONT_LEFT);
CONVERT_MASK(AudioObjectType_TopFrontRight, SPEAKER_TOP_FRONT_RIGHT);
CONVERT_MASK(AudioObjectType_TopBackLeft, SPEAKER_TOP_BACK_LEFT);
CONVERT_MASK(AudioObjectType_TopBackRight, SPEAKER_TOP_BACK_RIGHT);
CONVERT_MASK(AudioObjectType_BackCenter, SPEAKER_BACK_CENTER);
}
static HRESULT activate_stream(SpatialAudioStreamImpl *stream)
{
WAVEFORMATEXTENSIBLE *object_fmtex = (WAVEFORMATEXTENSIBLE *)stream->params.ObjectFormat;
HRESULT hr;
REFERENCE_TIME period;
if(!(object_fmtex->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
(object_fmtex->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
IsEqualGUID(&object_fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))){
FIXME("Only float formats are supported for now\n");
return E_INVALIDARG;
}
hr = IMMDevice_Activate(stream->sa_client->mmdev, &IID_IAudioClient,
CLSCTX_INPROC_SERVER, NULL, (void**)&stream->client);
if(FAILED(hr)){
WARN("Activate failed: %08lx\n", hr);
return hr;
}
hr = IAudioClient_GetDevicePeriod(stream->client, &period, NULL);
if(FAILED(hr)){
WARN("GetDevicePeriod failed: %08lx\n", hr);
IAudioClient_Release(stream->client);
return hr;
}
stream->stream_fmtex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
static_mask_to_channels(stream->params.StaticObjectTypeMask,
&stream->stream_fmtex.Format.nChannels, &stream->stream_fmtex.dwChannelMask,
stream->static_object_map);
stream->stream_fmtex.Format.nSamplesPerSec = stream->params.ObjectFormat->nSamplesPerSec;
stream->stream_fmtex.Format.wBitsPerSample = stream->params.ObjectFormat->wBitsPerSample;
stream->stream_fmtex.Format.nBlockAlign = (stream->stream_fmtex.Format.nChannels * stream->stream_fmtex.Format.wBitsPerSample) / 8;
stream->stream_fmtex.Format.nAvgBytesPerSec = stream->stream_fmtex.Format.nSamplesPerSec * stream->stream_fmtex.Format.nBlockAlign;
stream->stream_fmtex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
stream->stream_fmtex.Samples.wValidBitsPerSample = stream->stream_fmtex.Format.wBitsPerSample;
stream->stream_fmtex.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
hr = IAudioClient_Initialize(stream->client, AUDCLNT_SHAREMODE_SHARED,
AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
period, 0, &stream->stream_fmtex.Format, NULL);
if(FAILED(hr)){
WARN("Initialize failed: %08lx\n", hr);
IAudioClient_Release(stream->client);
return hr;
}
hr = IAudioClient_SetEventHandle(stream->client, stream->params.EventHandle);
if(FAILED(hr)){
WARN("SetEventHandle failed: %08lx\n", hr);
IAudioClient_Release(stream->client);
return hr;
}
hr = IAudioClient_GetService(stream->client, &IID_IAudioRenderClient, (void**)&stream->render);
if(FAILED(hr)){
WARN("GetService(AudioRenderClient) failed: %08lx\n", hr);
IAudioClient_Release(stream->client);
return hr;
}
stream->period_frames = MulDiv(period, stream->stream_fmtex.Format.nSamplesPerSec, 10000000);
return S_OK;
}
static HRESULT WINAPI SAC_ActivateSpatialAudioStream(ISpatialAudioClient *iface,
const PROPVARIANT *prop, REFIID riid, void **stream)
{
SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
SpatialAudioObjectRenderStreamActivationParams *params;
HRESULT hr;
TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), stream);
if(IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStream)){
SpatialAudioStreamImpl *obj;
if(prop &&
(prop->vt != VT_BLOB ||
prop->blob.cbSize != sizeof(SpatialAudioObjectRenderStreamActivationParams))){
WARN("Got invalid params\n");
*stream = NULL;
return E_INVALIDARG;
}
params = (SpatialAudioObjectRenderStreamActivationParams*) prop->blob.pBlobData;
if(params->StaticObjectTypeMask & AudioObjectType_Dynamic){
*stream = NULL;
return E_INVALIDARG;
}
if(params->EventHandle == INVALID_HANDLE_VALUE ||
params->EventHandle == 0){
*stream = NULL;
return E_INVALIDARG;
}
if(!params->ObjectFormat ||
memcmp(params->ObjectFormat, &This->object_fmtex.Format, sizeof(*params->ObjectFormat) + params->ObjectFormat->cbSize)){
*stream = NULL;
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
obj = heap_alloc_zero(sizeof(SpatialAudioStreamImpl));
obj->ISpatialAudioObjectRenderStream_iface.lpVtbl = &ISpatialAudioObjectRenderStream_vtbl;
obj->ref = 1;
memcpy(&obj->params, params, sizeof(obj->params));
obj->update_frames = ~0;
InitializeCriticalSection(&obj->lock);
list_init(&obj->objects);
obj->sa_client = This;
SAC_AddRef(&This->ISpatialAudioClient_iface);
obj->params.ObjectFormat = clone_fmtex(obj->params.ObjectFormat);
DuplicateHandle(GetCurrentProcess(), obj->params.EventHandle,
GetCurrentProcess(), &obj->params.EventHandle, 0, FALSE,
DUPLICATE_SAME_ACCESS);
if(obj->params.NotifyObject)
ISpatialAudioObjectRenderStreamNotify_AddRef(obj->params.NotifyObject);
if(TRACE_ON(mmdevapi)){
TRACE("ObjectFormat: {%s}\n", debugstr_fmtex(obj->params.ObjectFormat));
TRACE("StaticObjectTypeMask: 0x%x\n", obj->params.StaticObjectTypeMask);
TRACE("MinDynamicObjectCount: 0x%x\n", obj->params.MinDynamicObjectCount);
TRACE("MaxDynamicObjectCount: 0x%x\n", obj->params.MaxDynamicObjectCount);
TRACE("Category: 0x%x\n", obj->params.Category);
TRACE("EventHandle: %p\n", obj->params.EventHandle);
TRACE("NotifyObject: %p\n", obj->params.NotifyObject);
}
hr = activate_stream(obj);
if(FAILED(hr)){
if(obj->params.NotifyObject)
ISpatialAudioObjectRenderStreamNotify_Release(obj->params.NotifyObject);
DeleteCriticalSection(&obj->lock);
heap_free((void*)obj->params.ObjectFormat);
CloseHandle(obj->params.EventHandle);
ISpatialAudioClient_Release(&obj->sa_client->ISpatialAudioClient_iface);
heap_free(obj);
*stream = NULL;
return hr;
}
*stream = &obj->ISpatialAudioObjectRenderStream_iface;
}else{
FIXME("Unsupported audio stream IID: %s\n", debugstr_guid(riid));
*stream = NULL;
return E_NOTIMPL;
}
return S_OK;
}
static ISpatialAudioClientVtbl ISpatialAudioClient_vtbl = {
SAC_QueryInterface,
SAC_AddRef,
SAC_Release,
SAC_GetStaticObjectPosition,
SAC_GetNativeStaticObjectTypeMask,
SAC_GetMaxDynamicObjectCount,
SAC_GetSupportedAudioObjectFormatEnumerator,
SAC_GetMaxFrameCount,
SAC_IsAudioObjectFormatSupported,
SAC_IsSpatialAudioStreamAvailable,
SAC_ActivateSpatialAudioStream,
};
static HRESULT WINAPI SAOFE_QueryInterface(IAudioFormatEnumerator *iface,
REFIID riid, void **ppvObject)
{
SpatialAudioImpl *This = impl_from_IAudioFormatEnumerator(iface);
return SAC_QueryInterface(&This->ISpatialAudioClient_iface, riid, ppvObject);
}
static ULONG WINAPI SAOFE_AddRef(IAudioFormatEnumerator *iface)
{
SpatialAudioImpl *This = impl_from_IAudioFormatEnumerator(iface);
return SAC_AddRef(&This->ISpatialAudioClient_iface);
}
static ULONG WINAPI SAOFE_Release(IAudioFormatEnumerator *iface)
{
SpatialAudioImpl *This = impl_from_IAudioFormatEnumerator(iface);
return SAC_Release(&This->ISpatialAudioClient_iface);
}
static HRESULT WINAPI SAOFE_GetCount(IAudioFormatEnumerator *iface, UINT32 *count)
{
SpatialAudioImpl *This = impl_from_IAudioFormatEnumerator(iface);
TRACE("(%p)->(%p)\n", This, count);
*count = 1;
return S_OK;
}
static HRESULT WINAPI SAOFE_GetFormat(IAudioFormatEnumerator *iface,
UINT32 index, WAVEFORMATEX **format)
{
SpatialAudioImpl *This = impl_from_IAudioFormatEnumerator(iface);
TRACE("(%p)->(%u, %p)\n", This, index, format);
if(index > 0)
return E_INVALIDARG;
*format = &This->object_fmtex.Format;
return S_OK;
}
static IAudioFormatEnumeratorVtbl IAudioFormatEnumerator_vtbl = {
SAOFE_QueryInterface,
SAOFE_AddRef,
SAOFE_Release,
SAOFE_GetCount,
SAOFE_GetFormat,
};
HRESULT SpatialAudioClient_Create(IMMDevice *mmdev, ISpatialAudioClient **out)
{
SpatialAudioImpl *obj;
IAudioClient *aclient;
WAVEFORMATEX *closest;
HRESULT hr;
obj = heap_alloc_zero(sizeof(*obj));
obj->ref = 1;
obj->ISpatialAudioClient_iface.lpVtbl = &ISpatialAudioClient_vtbl;
obj->IAudioFormatEnumerator_iface.lpVtbl = &IAudioFormatEnumerator_vtbl;
obj->object_fmtex.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
obj->object_fmtex.Format.nChannels = 1;
obj->object_fmtex.Format.nSamplesPerSec = 48000;
obj->object_fmtex.Format.wBitsPerSample = sizeof(float) * 8;
obj->object_fmtex.Format.nBlockAlign = (obj->object_fmtex.Format.nChannels * obj->object_fmtex.Format.wBitsPerSample) / 8;
obj->object_fmtex.Format.nAvgBytesPerSec = obj->object_fmtex.Format.nSamplesPerSec * obj->object_fmtex.Format.nBlockAlign;
obj->object_fmtex.Format.cbSize = 0;
hr = IMMDevice_Activate(mmdev, &IID_IAudioClient,
CLSCTX_INPROC_SERVER, NULL, (void**)&aclient);
if(FAILED(hr)){
WARN("Activate failed: %08lx\n", hr);
heap_free(obj);
return hr;
}
hr = IAudioClient_IsFormatSupported(aclient, AUDCLNT_SHAREMODE_SHARED, &obj->object_fmtex.Format, &closest);
IAudioClient_Release(aclient);
if(hr == S_FALSE){
if(sizeof(WAVEFORMATEX) + closest->cbSize > sizeof(obj->object_fmtex)){
ERR("Returned format too large: %s\n", debugstr_fmtex(closest));
CoTaskMemFree(closest);
heap_free(obj);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}else if(!((closest->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
(closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
IsEqualGUID(&((WAVEFORMATEXTENSIBLE *)closest)->SubFormat,
&KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) &&
closest->wBitsPerSample == 32)){
ERR("Returned format not 32-bit float: %s\n", debugstr_fmtex(closest));
CoTaskMemFree(closest);
heap_free(obj);
return AUDCLNT_E_UNSUPPORTED_FORMAT;
}
WARN("The audio stack doesn't support 48kHz 32bit float. Using the closest match. Audio may be glitchy. %s\n", debugstr_fmtex(closest));
memcpy(&obj->object_fmtex,
closest,
sizeof(WAVEFORMATEX) + closest->cbSize);
CoTaskMemFree(closest);
} else if(hr != S_OK){
WARN("Checking supported formats failed: %08lx\n", hr);
heap_free(obj);
return hr;
}
obj->mmdev = mmdev;
IMMDevice_AddRef(mmdev);
*out = &obj->ISpatialAudioClient_iface;
return S_OK;
}

View file

@ -0,0 +1,10 @@
TESTDLL = mmdevapi.dll
IMPORTS = ole32 version user32 advapi32 winmm
C_SRCS = \
capture.c \
dependency.c \
mmdevenum.c \
propstore.c \
render.c \
spatialaudio.c

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,98 @@
/*
* Copyright 2009 Maarten Lankhorst
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "wine/test.h"
#define COBJMACROS
#ifdef STANDALONE
#include "initguid.h"
#endif
#include "unknwn.h"
#include "uuids.h"
#include "mmdeviceapi.h"
#include "dshow.h"
#include "dsound.h"
START_TEST(dependency)
{
HRESULT hr;
IMMDeviceEnumerator *mme = NULL;
IMMDevice *dev = NULL;
IDirectSound8 *ds8 = NULL;
IBaseFilter *bf = NULL;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&mme);
if (FAILED(hr))
{
skip("mmdevapi not available: 0x%08lx\n", hr);
goto cleanup;
}
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(mme, eRender, eMultimedia, &dev);
ok(hr == S_OK || hr == E_NOTFOUND, "GetDefaultAudioEndpoint failed: 0x%08lx\n", hr);
if (hr != S_OK)
{
if (hr == E_NOTFOUND)
skip("No sound card available\n");
else
skip("GetDefaultAudioEndpoint returns 0x%08lx\n", hr);
goto cleanup;
}
ok(!GetModuleHandleA("dsound.dll"), "dsound.dll was already loaded!\n");
hr = IMMDevice_Activate(dev, &IID_IDirectSound8, CLSCTX_INPROC_SERVER, NULL, (void**)&ds8);
ok(hr == S_OK, "Activating ds8 interface failed: 0x%08lx\n", hr);
if (hr == S_OK)
{
ok(GetModuleHandleA("dsound.dll") != NULL, "dsound.dll not loaded!\n");
ok(ds8 != NULL, "ds8 pointer is null\n");
}
if (ds8)
IDirectSound8_Release(ds8);
ok(!GetModuleHandleA("quartz.dll"), "quartz.dll was already loaded!\n");
hr = IMMDevice_Activate(dev, &IID_IBaseFilter, CLSCTX_INPROC_SERVER, NULL, (void**)&bf);
ok(hr == S_OK, "Activating bf failed: 0x%08lx\n", hr);
if (hr == S_OK)
{
ok(GetModuleHandleA("quartz.dll") != NULL, "quartz.dll not loaded!\n");
ok(bf != NULL, "bf pointer is null\n");
if (bf)
{
CLSID clsid;
hr = IBaseFilter_GetClassID(bf, &clsid);
ok(hr == S_OK, "GetClassId failed with 0x%08lx\n", hr);
if (hr == S_OK)
ok(IsEqualCLSID(&clsid, &CLSID_DSoundRender), "Wrong class id %s\n", wine_dbgstr_guid(&clsid));
}
}
cleanup:
if (bf)
IBaseFilter_Release(bf);
if (dev)
IMMDevice_Release(dev);
if (mme)
IMMDeviceEnumerator_Release(mme);
CoUninitialize();
}

View file

@ -0,0 +1,484 @@
/*
* Copyright 2009 Maarten Lankhorst
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "wine/test.h"
#define COBJMACROS
#include "initguid.h"
#include "endpointvolume.h"
#include "mmdeviceapi.h"
#include "audioclient.h"
#include "spatialaudioclient.h"
#include "audiopolicy.h"
#include "dshow.h"
#include "dsound.h"
#include "devpkey.h"
DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
static UINT g_num_mmdevs;
static WCHAR g_device_path[MAX_PATH];
/* Some of the QueryInterface tests are really just to check if I got the IIDs right :) */
/* IMMDeviceCollection appears to have no QueryInterface method and instead forwards to mme */
static void test_collection(IMMDeviceEnumerator *mme, IMMDeviceCollection *col)
{
IMMDeviceCollection *col2;
IMMDeviceEnumerator *mme2;
IUnknown *unk;
HRESULT hr;
ULONG ref;
UINT numdev;
IMMDevice *dev;
/* collection doesn't keep a ref on parent */
IMMDeviceEnumerator_AddRef(mme);
ref = IMMDeviceEnumerator_Release(mme);
ok(ref == 2, "Reference count on parent is %lu\n", ref);
ref = IMMDeviceCollection_AddRef(col);
IMMDeviceCollection_Release(col);
ok(ref == 2, "Invalid reference count %lu on collection\n", ref);
hr = IMMDeviceCollection_QueryInterface(col, &IID_IUnknown, NULL);
ok(hr == E_POINTER, "Null ppv returns %08lx\n", hr);
hr = IMMDeviceCollection_QueryInterface(col, &IID_IUnknown, (void**)&unk);
ok(hr == S_OK, "Cannot query for IID_IUnknown: 0x%08lx\n", hr);
if (hr == S_OK)
{
ok((IUnknown*)col == unk, "Pointers are not identical %p/%p/%p\n", col, unk, mme);
IUnknown_Release(unk);
}
hr = IMMDeviceCollection_QueryInterface(col, &IID_IMMDeviceCollection, (void**)&col2);
ok(hr == S_OK, "Cannot query for IID_IMMDeviceCollection: 0x%08lx\n", hr);
if (hr == S_OK)
IMMDeviceCollection_Release(col2);
hr = IMMDeviceCollection_QueryInterface(col, &IID_IMMDeviceEnumerator, (void**)&mme2);
ok(hr == E_NOINTERFACE, "Query for IID_IMMDeviceEnumerator returned: 0x%08lx\n", hr);
if (hr == S_OK)
IMMDeviceEnumerator_Release(mme2);
hr = IMMDeviceCollection_GetCount(col, NULL);
ok(hr == E_POINTER, "GetCount returned 0x%08lx\n", hr);
hr = IMMDeviceCollection_GetCount(col, &numdev);
ok(hr == S_OK, "GetCount returned 0x%08lx\n", hr);
dev = (void*)(LONG_PTR)0x12345678;
hr = IMMDeviceCollection_Item(col, numdev, &dev);
ok(hr == E_INVALIDARG, "Asking for too high device returned 0x%08lx\n", hr);
ok(dev == NULL, "Returned non-null device\n");
g_num_mmdevs = numdev;
if (numdev)
{
hr = IMMDeviceCollection_Item(col, 0, NULL);
ok(hr == E_POINTER, "Query with null pointer returned 0x%08lx\n", hr);
hr = IMMDeviceCollection_Item(col, 0, &dev);
ok(hr == S_OK, "Valid Item returned 0x%08lx\n", hr);
ok(dev != NULL, "Device is null!\n");
if (dev != NULL)
{
char temp[128];
WCHAR *id = NULL;
if (IMMDevice_GetId(dev, &id) == S_OK)
{
IMMDevice *dev2;
lstrcpyW(g_device_path, id);
temp[sizeof(temp)-1] = 0;
WideCharToMultiByte(CP_ACP, 0, id, -1, temp, sizeof(temp)-1, NULL, NULL);
trace("Device found: %s\n", temp);
hr = IMMDeviceEnumerator_GetDevice(mme, id, &dev2);
ok(hr == S_OK, "GetDevice failed: %08lx\n", hr);
IMMDevice_Release(dev2);
CoTaskMemFree(id);
}
}
if (dev)
IMMDevice_Release(dev);
}
IMMDeviceCollection_Release(col);
}
static struct {
LONG ref;
HANDLE evt;
CRITICAL_SECTION lock;
IActivateAudioInterfaceAsyncOperation *op;
DWORD main_tid;
char msg_pfx[128];
IUnknown *result_iface;
HRESULT result_hr;
} async_activate_test;
static HRESULT WINAPI async_activate_QueryInterface(
IActivateAudioInterfaceCompletionHandler *iface,
REFIID riid,
void **ppvObject)
{
if(IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IAgileObject) ||
IsEqualIID(riid, &IID_IActivateAudioInterfaceCompletionHandler)){
*ppvObject = iface;
IUnknown_AddRef((IUnknown*)*ppvObject);
return S_OK;
}
*ppvObject = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI async_activate_AddRef(
IActivateAudioInterfaceCompletionHandler *iface)
{
return InterlockedIncrement(&async_activate_test.ref);
}
static ULONG WINAPI async_activate_Release(
IActivateAudioInterfaceCompletionHandler *iface)
{
ULONG ref = InterlockedDecrement(&async_activate_test.ref);
if(ref == 1)
SetEvent(async_activate_test.evt);
return ref;
}
static HRESULT WINAPI async_activate_ActivateCompleted(
IActivateAudioInterfaceCompletionHandler *iface,
IActivateAudioInterfaceAsyncOperation *op)
{
HRESULT hr;
EnterCriticalSection(&async_activate_test.lock);
ok(op == async_activate_test.op,
"%s: Got different completion operation\n",
async_activate_test.msg_pfx);
LeaveCriticalSection(&async_activate_test.lock);
ok(GetCurrentThreadId() != async_activate_test.main_tid,
"%s: Expected callback on worker thread\n",
async_activate_test.msg_pfx);
hr = IActivateAudioInterfaceAsyncOperation_GetActivateResult(op,
&async_activate_test.result_hr, &async_activate_test.result_iface);
ok(hr == S_OK,
"%s: GetActivateResult failed: %08lx\n",
async_activate_test.msg_pfx, hr);
return S_OK;
}
static IActivateAudioInterfaceCompletionHandlerVtbl async_activate_vtbl = {
async_activate_QueryInterface,
async_activate_AddRef,
async_activate_Release,
async_activate_ActivateCompleted,
};
static IActivateAudioInterfaceCompletionHandler async_activate_done = {
&async_activate_vtbl
};
static void test_ActivateAudioInterfaceAsync(void)
{
HRESULT (* WINAPI pActivateAudioInterfaceAsync)(const WCHAR *path,
REFIID riid, PROPVARIANT *params,
IActivateAudioInterfaceCompletionHandler *done_handler,
IActivateAudioInterfaceAsyncOperation **op);
HANDLE h_mmdev;
HRESULT hr;
LPOLESTR path;
DWORD dr;
IAudioClient3 *ac3;
h_mmdev = LoadLibraryA("mmdevapi.dll");
pActivateAudioInterfaceAsync = (void*)GetProcAddress(h_mmdev, "ActivateAudioInterfaceAsync");
if (!pActivateAudioInterfaceAsync)
{
win_skip("ActivateAudioInterfaceAsync is not supported on Win <= 7\n");
return;
}
/* some applications look this up by ordinal */
pActivateAudioInterfaceAsync = (void*)GetProcAddress(h_mmdev, (char *)17);
ok(pActivateAudioInterfaceAsync != NULL, "mmdevapi.ActivateAudioInterfaceAsync missing!\n");
async_activate_test.ref = 1;
async_activate_test.evt = CreateEventW(NULL, FALSE, FALSE, NULL);
InitializeCriticalSection(&async_activate_test.lock);
async_activate_test.op = NULL;
async_activate_test.main_tid = GetCurrentThreadId();
async_activate_test.result_iface = NULL;
async_activate_test.result_hr = 0;
/* try invalid device path */
strcpy(async_activate_test.msg_pfx, "invalid_path");
EnterCriticalSection(&async_activate_test.lock);
hr = pActivateAudioInterfaceAsync(L"winetest_bogus", &IID_IAudioClient3, NULL, &async_activate_done, &async_activate_test.op);
ok(hr == S_OK, "ActivateAudioInterfaceAsync failed: %08lx\n", hr);
LeaveCriticalSection(&async_activate_test.lock);
IActivateAudioInterfaceAsyncOperation_Release(async_activate_test.op);
dr = WaitForSingleObject(async_activate_test.evt, 1000); /* wait for all refs other than our own to be released */
ok(dr == WAIT_OBJECT_0, "Timed out waiting for async activate to complete\n");
ok(async_activate_test.ref == 1, "ActivateAudioInterfaceAsync leaked a handler ref: %lu\n", async_activate_test.ref);
ok(async_activate_test.result_hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
"mmdevice activation gave wrong result: %08lx\n", async_activate_test.result_hr);
ok(async_activate_test.result_iface == NULL, "Got non-NULL iface pointer: %p\n", async_activate_test.result_iface);
/* device id from IMMDevice does not work */
if(g_num_mmdevs > 0){
strcpy(async_activate_test.msg_pfx, "mmdevice_id");
EnterCriticalSection(&async_activate_test.lock);
hr = pActivateAudioInterfaceAsync(g_device_path, &IID_IAudioClient3, NULL, &async_activate_done, &async_activate_test.op);
ok(hr == S_OK, "ActivateAudioInterfaceAsync failed: %08lx\n", hr);
LeaveCriticalSection(&async_activate_test.lock);
IActivateAudioInterfaceAsyncOperation_Release(async_activate_test.op);
dr = WaitForSingleObject(async_activate_test.evt, 1000);
ok(dr == WAIT_OBJECT_0, "Timed out waiting for async activate to complete\n");
ok(async_activate_test.ref == 1, "ActivateAudioInterfaceAsync leaked a handler ref: %lu\n", async_activate_test.ref);
ok(async_activate_test.result_hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND),
"mmdevice activation gave wrong result: %08lx\n", async_activate_test.result_hr);
ok(async_activate_test.result_iface == NULL, "Got non-NULL iface pointer: %p\n", async_activate_test.result_iface);
}
/* try DEVINTERFACE_AUDIO_RENDER */
strcpy(async_activate_test.msg_pfx, "audio_render");
StringFromIID(&DEVINTERFACE_AUDIO_RENDER, &path);
EnterCriticalSection(&async_activate_test.lock);
hr = pActivateAudioInterfaceAsync(path, &IID_IAudioClient3, NULL, &async_activate_done, &async_activate_test.op);
ok(hr == S_OK, "ActivateAudioInterfaceAsync failed: %08lx\n", hr);
LeaveCriticalSection(&async_activate_test.lock);
IActivateAudioInterfaceAsyncOperation_Release(async_activate_test.op);
dr = WaitForSingleObject(async_activate_test.evt, 1000);
ok(dr == WAIT_OBJECT_0, "Timed out waiting for async activate to complete\n");
ok(async_activate_test.ref == 1, "ActivateAudioInterfaceAsync leaked a handler ref\n");
ok(async_activate_test.result_hr == S_OK ||
(g_num_mmdevs == 0 && async_activate_test.result_hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || /* no devices */
broken(async_activate_test.result_hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)), /* win8 doesn't support DEVINTERFACE_AUDIO_RENDER */
"mmdevice activation gave wrong result: %08lx\n", async_activate_test.result_hr);
if(async_activate_test.result_hr == S_OK){
ok(async_activate_test.result_iface != NULL, "Got NULL iface pointer on success?\n");
/* returned iface should be the IID we requested */
hr = IUnknown_QueryInterface(async_activate_test.result_iface, &IID_IAudioClient3, (void**)&ac3);
ok(hr == S_OK, "Failed to query IAudioClient3: %08lx\n", hr);
ok(async_activate_test.result_iface == (IUnknown*)ac3,
"Activated interface other than IAudioClient3!\n");
IAudioClient3_Release(ac3);
IUnknown_Release(async_activate_test.result_iface);
}
CoTaskMemFree(path);
CloseHandle(async_activate_test.evt);
DeleteCriticalSection(&async_activate_test.lock);
}
static HRESULT WINAPI notif_QueryInterface(IMMNotificationClient *iface,
const GUID *riid, void **obj)
{
ok(0, "Unexpected QueryInterface call\n");
return E_NOTIMPL;
}
static ULONG WINAPI notif_AddRef(IMMNotificationClient *iface)
{
ok(0, "Unexpected AddRef call\n");
return 2;
}
static ULONG WINAPI notif_Release(IMMNotificationClient *iface)
{
ok(0, "Unexpected Release call\n");
return 1;
}
static HRESULT WINAPI notif_OnDeviceStateChanged(IMMNotificationClient *iface,
const WCHAR *device_id, DWORD new_state)
{
ok(0, "Unexpected OnDeviceStateChanged call\n");
return E_NOTIMPL;
}
static HRESULT WINAPI notif_OnDeviceAdded(IMMNotificationClient *iface,
const WCHAR *device_id)
{
ok(0, "Unexpected OnDeviceAdded call\n");
return E_NOTIMPL;
}
static HRESULT WINAPI notif_OnDeviceRemoved(IMMNotificationClient *iface,
const WCHAR *device_id)
{
ok(0, "Unexpected OnDeviceRemoved call\n");
return E_NOTIMPL;
}
static HRESULT WINAPI notif_OnDefaultDeviceChanged(IMMNotificationClient *iface,
EDataFlow flow, ERole role, const WCHAR *device_id)
{
ok(0, "Unexpected OnDefaultDeviceChanged call\n");
return E_NOTIMPL;
}
static HRESULT WINAPI notif_OnPropertyValueChanged(IMMNotificationClient *iface,
const WCHAR *device_id, const PROPERTYKEY key)
{
ok(0, "Unexpected OnPropertyValueChanged call\n");
return E_NOTIMPL;
}
static IMMNotificationClientVtbl notif_vtbl = {
notif_QueryInterface,
notif_AddRef,
notif_Release,
notif_OnDeviceStateChanged,
notif_OnDeviceAdded,
notif_OnDeviceRemoved,
notif_OnDefaultDeviceChanged,
notif_OnPropertyValueChanged
};
static IMMNotificationClient notif = { &notif_vtbl };
/* Only do parameter tests here, the actual MMDevice testing should be a separate test */
START_TEST(mmdevenum)
{
HRESULT hr;
IUnknown *unk = NULL;
IMMDeviceEnumerator *mme, *mme2;
ULONG ref;
IMMDeviceCollection *col;
IMMDevice *dev;
CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&mme);
if (FAILED(hr))
{
skip("mmdevapi not available: 0x%08lx\n", hr);
return;
}
/* Odd behavior.. bug? */
ref = IMMDeviceEnumerator_AddRef(mme);
ok(ref == 3, "Invalid reference count after incrementing: %lu\n", ref);
IMMDeviceEnumerator_Release(mme);
hr = IMMDeviceEnumerator_QueryInterface(mme, &IID_IUnknown, (void**)&unk);
ok(hr == S_OK, "returned 0x%08lx\n", hr);
if (hr != S_OK) return;
ok( (LONG_PTR)mme == (LONG_PTR)unk, "Pointers are unequal %p/%p\n", unk, mme);
IUnknown_Release(unk);
/* Proving that it is static.. */
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&mme2);
ok(hr == S_OK, "CoCreateInstance failed: 0x%08lx\n", hr);
IMMDeviceEnumerator_Release(mme2);
ok(mme == mme2, "Pointers are not equal!\n");
hr = IMMDeviceEnumerator_QueryInterface(mme, &IID_IUnknown, NULL);
ok(hr == E_POINTER, "Null pointer on QueryInterface returned %08lx\n", hr);
hr = IMMDeviceEnumerator_QueryInterface(mme, &GUID_NULL, (void**)&unk);
ok(!unk, "Unk not reset to null after invalid QI\n");
ok(hr == E_NOINTERFACE, "Invalid hr %08lx returned on IID_NULL\n", hr);
hr = IMMDeviceEnumerator_GetDevice(mme, L"notadevice", NULL);
ok(hr == E_POINTER, "GetDevice gave wrong error: %08lx\n", hr);
hr = IMMDeviceEnumerator_GetDevice(mme, NULL, &dev);
ok(hr == E_POINTER, "GetDevice gave wrong error: %08lx\n", hr);
hr = IMMDeviceEnumerator_GetDevice(mme, L"notadevice", &dev);
ok(hr == E_INVALIDARG, "GetDevice gave wrong error: %08lx\n", hr);
col = (void*)(LONG_PTR)0x12345678;
hr = IMMDeviceEnumerator_EnumAudioEndpoints(mme, 0xffff, DEVICE_STATEMASK_ALL, &col);
ok(hr == E_INVALIDARG, "Setting invalid data flow returned 0x%08lx\n", hr);
ok(col == NULL, "Collection pointer non-null on failure\n");
hr = IMMDeviceEnumerator_EnumAudioEndpoints(mme, eAll, DEVICE_STATEMASK_ALL+1, &col);
ok(hr == E_INVALIDARG, "Setting invalid mask returned 0x%08lx\n", hr);
hr = IMMDeviceEnumerator_EnumAudioEndpoints(mme, eAll, DEVICE_STATEMASK_ALL, NULL);
ok(hr == E_POINTER, "Invalid pointer returned: 0x%08lx\n", hr);
hr = IMMDeviceEnumerator_EnumAudioEndpoints(mme, eAll, DEVICE_STATEMASK_ALL, &col);
ok(hr == S_OK, "Valid EnumAudioEndpoints returned 0x%08lx\n", hr);
if (hr == S_OK)
{
ok(!!col, "Returned null pointer\n");
if (col)
test_collection(mme, col);
}
hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(mme, NULL);
ok(hr == E_POINTER, "RegisterEndpointNotificationCallback failed: %08lx\n", hr);
hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(mme, &notif);
ok(hr == S_OK, "RegisterEndpointNotificationCallback failed: %08lx\n", hr);
hr = IMMDeviceEnumerator_RegisterEndpointNotificationCallback(mme, &notif);
ok(hr == S_OK, "RegisterEndpointNotificationCallback failed: %08lx\n", hr);
hr = IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(mme, NULL);
ok(hr == E_POINTER, "UnregisterEndpointNotificationCallback failed: %08lx\n", hr);
hr = IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(mme, (IMMNotificationClient*)0xdeadbeef);
ok(hr == E_NOTFOUND, "UnregisterEndpointNotificationCallback failed: %08lx\n", hr);
hr = IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(mme, &notif);
ok(hr == S_OK, "UnregisterEndpointNotificationCallback failed: %08lx\n", hr);
hr = IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(mme, &notif);
ok(hr == S_OK, "UnregisterEndpointNotificationCallback failed: %08lx\n", hr);
hr = IMMDeviceEnumerator_UnregisterEndpointNotificationCallback(mme, &notif);
ok(hr == E_NOTFOUND, "UnregisterEndpointNotificationCallback failed: %08lx\n", hr);
IMMDeviceEnumerator_Release(mme);
test_ActivateAudioInterfaceAsync();
}

View file

@ -0,0 +1,254 @@
/*
* Copyright 2010 Maarten Lankhorst 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 NONAMELESSUNION
#include "wine/test.h"
#define COBJMACROS
#ifdef STANDALONE
#include "initguid.h"
#endif
#include "unknwn.h"
#include "uuids.h"
#include "mmdeviceapi.h"
#include "devpkey.h"
static BOOL (WINAPI *pIsWow64Process)(HANDLE, BOOL *);
static const WCHAR software_renderW[] =
L"Software\\Microsoft\\Windows\\CurrentVersion\\MMDevices\\Audio\\Render";
static void test_propertystore(IPropertyStore *store)
{
HRESULT hr;
PROPVARIANT pv;
char temp[128];
temp[sizeof(temp)-1] = 0;
pv.vt = VT_EMPTY;
hr = IPropertyStore_GetValue(store, &PKEY_AudioEndpoint_GUID, &pv);
ok(hr == S_OK, "Failed with %08lx\n", hr);
ok(pv.vt == VT_LPWSTR, "Value should be %i, is %i\n", VT_LPWSTR, pv.vt);
if (hr == S_OK && pv.vt == VT_LPWSTR)
{
WideCharToMultiByte(CP_ACP, 0, pv.pwszVal, -1, temp, sizeof(temp)-1, NULL, NULL);
trace("guid: %s\n", temp);
PropVariantClear(&pv);
}
pv.vt = VT_EMPTY;
hr = IPropertyStore_GetValue(store, (const PROPERTYKEY*)&DEVPKEY_DeviceInterface_FriendlyName, &pv);
ok(hr == S_OK, "Failed with %08lx\n", hr);
ok(pv.vt == VT_LPWSTR && pv.pwszVal, "FriendlyName value had wrong type: 0x%x or was NULL\n", pv.vt);
PropVariantClear(&pv);
pv.vt = VT_EMPTY;
hr = IPropertyStore_GetValue(store, (const PROPERTYKEY*)&DEVPKEY_DeviceInterface_Enabled, &pv);
ok(hr == S_OK, "Failed with %08lx\n", hr);
ok(pv.vt == VT_EMPTY, "Key should not be found\n");
PropVariantClear(&pv);
pv.vt = VT_EMPTY;
hr = IPropertyStore_GetValue(store, (const PROPERTYKEY*)&DEVPKEY_DeviceInterface_ClassGuid, &pv);
ok(hr == S_OK, "Failed with %08lx\n", hr);
ok(pv.vt == VT_EMPTY, "Key should not be found\n");
PropVariantClear(&pv);
}
static void test_deviceinterface(IPropertyStore *store)
{
HRESULT hr;
PROPVARIANT pv;
static const PROPERTYKEY deviceinterface_key = {
{0x233164c8, 0x1b2c, 0x4c7d, {0xbc, 0x68, 0xb6, 0x71, 0x68, 0x7a, 0x25, 0x67}}, 1
};
pv.vt = VT_EMPTY;
hr = IPropertyStore_GetValue(store, &deviceinterface_key, &pv);
ok(hr == S_OK, "GetValue failed: %08lx\n", hr);
ok(pv.vt == VT_LPWSTR, "Got wrong variant type: 0x%x\n", pv.vt);
trace("device interface: %s\n", wine_dbgstr_w(pv.pwszVal));
PropVariantClear(&pv);
}
static void test_getat(IPropertyStore *store)
{
HRESULT hr;
DWORD propcount;
DWORD prop;
PROPERTYKEY pkey;
BOOL found_name = FALSE;
BOOL found_desc = FALSE;
char temp[128];
temp[sizeof(temp)-1] = 0;
hr = IPropertyStore_GetCount(store, &propcount);
ok(hr == S_OK, "Failed with %08lx\n", hr);
ok(propcount > 0, "Propcount %ld should be greater than zero\n", propcount);
for (prop = 0; prop < propcount; prop++) {
hr = IPropertyStore_GetAt(store, prop, &pkey);
ok(hr == S_OK, "Failed with %08lx\n", hr);
if (IsEqualPropertyKey(pkey, DEVPKEY_Device_FriendlyName))
found_name = TRUE;
if (IsEqualPropertyKey(pkey, DEVPKEY_Device_DeviceDesc))
found_desc = TRUE;
}
ok(found_name ||
broken(!found_name) /* vista */, "DEVPKEY_Device_FriendlyName not found\n");
ok(found_desc, "DEVPKEY_Device_DeviceDesc not found\n");
}
static void test_setvalue_on_wow64(IPropertyStore *store)
{
PROPVARIANT pv;
HRESULT hr;
LONG ret;
WCHAR *guidW;
HKEY root, props, devkey;
DWORD type, regval, size;
static const PROPERTYKEY PKEY_Bogus = {
{0x1da5d803, 0xd492, 0x4edd, {0x8c, 0x23, 0xe0, 0xc0, 0xff, 0xee, 0x7f, 0x00}}, 0x7f
};
static const WCHAR bogusW[] = L"{1DA5D803-D492-4EDD-8C23-E0C0FFEE7F00},127";
PropVariantInit(&pv);
pv.vt = VT_EMPTY;
hr = IPropertyStore_GetValue(store, &PKEY_AudioEndpoint_GUID, &pv);
ok(hr == S_OK, "Failed to get Endpoint GUID: %08lx\n", hr);
guidW = pv.pwszVal;
pv.vt = VT_UI4;
pv.ulVal = 0xAB;
hr = IPropertyStore_SetValue(store, &PKEY_Bogus, &pv);
ok(hr == S_OK || hr == E_ACCESSDENIED, "SetValue failed: %08lx\n", hr);
if (hr != S_OK)
{
win_skip("Missing permission to write to registry\n");
return;
}
pv.ulVal = 0x00;
hr = IPropertyStore_GetValue(store, &PKEY_Bogus, &pv);
ok(hr == S_OK, "GetValue failed: %08lx\n", hr);
ok(pv.ulVal == 0xAB, "Got wrong value: 0x%lx\n", pv.ulVal);
/* should find the key in 64-bit view */
ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, software_renderW, 0, KEY_READ|KEY_WOW64_64KEY, &root);
ok(ret == ERROR_SUCCESS, "Couldn't open mmdevices Render key: %lu\n", ret);
ret = RegOpenKeyExW(root, guidW, 0, KEY_READ|KEY_WOW64_64KEY, &devkey);
ok(ret == ERROR_SUCCESS, "Couldn't open mmdevice guid key: %lu\n", ret);
ret = RegOpenKeyExW(devkey, L"Properties", 0, KEY_READ|KEY_WOW64_64KEY, &props);
ok(ret == ERROR_SUCCESS, "Couldn't open mmdevice property key: %lu\n", ret);
/* Note: the registry key exists even without calling IPropStore::Commit */
size = sizeof(regval);
ret = RegQueryValueExW(props, bogusW, NULL, &type, (LPBYTE)&regval, &size);
ok(ret == ERROR_SUCCESS, "Couldn't get bogus propertykey value: %lu\n", ret);
ok(type == REG_DWORD, "Got wrong value type: %lu\n", type);
ok(regval == 0xAB, "Got wrong value: 0x%lx\n", regval);
RegCloseKey(props);
RegCloseKey(devkey);
RegCloseKey(root);
CoTaskMemFree(guidW);
/* should NOT find the key in 32-bit view */
ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, software_renderW, 0, KEY_READ, &root);
ok(ret == ERROR_FILE_NOT_FOUND, "Wrong error when opening mmdevices Render key: %lu\n", ret);
}
START_TEST(propstore)
{
HRESULT hr;
IMMDeviceEnumerator *mme = NULL;
IMMDevice *dev = NULL;
IPropertyStore *store;
BOOL is_wow64 = FALSE;
HMODULE hk32 = GetModuleHandleA("kernel32.dll");
pIsWow64Process = (void *)GetProcAddress(hk32, "IsWow64Process");
if (pIsWow64Process)
pIsWow64Process(GetCurrentProcess(), &is_wow64);
CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&mme);
if (FAILED(hr))
{
skip("mmdevapi not available: 0x%08lx\n", hr);
goto cleanup;
}
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(mme, eRender, eMultimedia, &dev);
ok(hr == S_OK || hr == E_NOTFOUND, "GetDefaultAudioEndpoint failed: 0x%08lx\n", hr);
if (hr != S_OK)
{
if (hr == E_NOTFOUND)
skip("No sound card available\n");
else
skip("GetDefaultAudioEndpoint returns 0x%08lx\n", hr);
goto cleanup;
}
store = NULL;
hr = IMMDevice_OpenPropertyStore(dev, 3, &store);
ok(hr == E_INVALIDARG, "Wrong hr returned: %08lx\n", hr);
if (hr != S_OK)
/* It seems on windows returning with E_INVALIDARG doesn't
* set store to NULL, so just don't set store to non-null
* before calling this function
*/
ok(!store, "Store set to non-NULL on failure: %p/%08lx\n", store, hr);
else if (store)
IPropertyStore_Release(store);
hr = IMMDevice_OpenPropertyStore(dev, STGM_READ, NULL);
ok(hr == E_POINTER, "Wrong hr returned: %08lx\n", hr);
store = NULL;
hr = IMMDevice_OpenPropertyStore(dev, STGM_READWRITE, &store);
if(hr == E_ACCESSDENIED)
hr = IMMDevice_OpenPropertyStore(dev, STGM_READ, &store);
ok(hr == S_OK, "Opening valid store returned %08lx\n", hr);
if (store)
{
test_propertystore(store);
test_deviceinterface(store);
test_getat(store);
if (is_wow64)
test_setvalue_on_wow64(store);
IPropertyStore_Release(store);
}
IMMDevice_Release(dev);
cleanup:
if (mme)
IMMDeviceEnumerator_Release(mme);
CoUninitialize();
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,506 @@
/*
* Copyright 2021 Arkadiusz Hiler for CodeWeavers
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <math.h>
#include <stdio.h>
#include "wine/test.h"
#define COBJMACROS
#ifdef STANDALONE
#include "initguid.h"
#endif
#include "mmdeviceapi.h"
#include "spatialaudioclient.h"
#include "mmsystem.h"
static IMMDeviceEnumerator *mme = NULL;
static IMMDevice *dev = NULL;
static ISpatialAudioClient *sac = NULL;
static UINT32 max_dyn_count;
static HANDLE event;
static WAVEFORMATEX format;
static void test_formats(void)
{
HRESULT hr;
IAudioFormatEnumerator *afe;
UINT32 format_count = 0;
WAVEFORMATEX *fmt = NULL;
hr = ISpatialAudioClient_GetSupportedAudioObjectFormatEnumerator(sac, &afe);
ok(hr == S_OK, "Getting format enumerator failed: 0x%08lx\n", hr);
hr = IAudioFormatEnumerator_GetCount(afe, &format_count);
ok(hr == S_OK, "Getting format count failed: 0x%08lx\n", hr);
ok(format_count == 1, "Got wrong format count, expected 1 got %u\n", format_count);
hr = IAudioFormatEnumerator_GetFormat(afe, 0, &fmt);
ok(hr == S_OK, "Getting format failed: 0x%08lx\n", hr);
ok(fmt != NULL, "Expected to get non-NULL format\n");
ok(fmt->wFormatTag == WAVE_FORMAT_IEEE_FLOAT, "Wrong format, expected WAVE_FORMAT_IEEE_FLOAT got %hx\n", fmt->wFormatTag);
ok(fmt->nChannels == 1, "Wrong number of channels, expected 1 got %hu\n", fmt->nChannels);
ok(fmt->nSamplesPerSec == 48000, "Wrong sample ret, expected 48000 got %lu\n", fmt->nSamplesPerSec);
ok(fmt->wBitsPerSample == 32, "Wrong bits per sample, expected 32 got %hu\n", fmt->wBitsPerSample);
ok(fmt->nBlockAlign == 4, "Wrong block align, expected 4 got %hu\n", fmt->nBlockAlign);
ok(fmt->nAvgBytesPerSec == 192000, "Wrong avg bytes per sec, expected 192000 got %lu\n", fmt->nAvgBytesPerSec);
ok(fmt->cbSize == 0, "Wrong cbSize for simple format, expected 0, got %hu\n", fmt->cbSize);
memcpy(&format, fmt, sizeof(format));
IAudioFormatEnumerator_Release(afe);
}
static void fill_activation_params(SpatialAudioObjectRenderStreamActivationParams *activation_params)
{
activation_params->StaticObjectTypeMask = \
AudioObjectType_FrontLeft |
AudioObjectType_FrontRight |
AudioObjectType_FrontCenter |
AudioObjectType_LowFrequency |
AudioObjectType_SideLeft |
AudioObjectType_SideRight |
AudioObjectType_BackLeft |
AudioObjectType_BackRight |
AudioObjectType_TopFrontLeft |
AudioObjectType_TopFrontRight |
AudioObjectType_TopBackLeft |
AudioObjectType_TopBackRight;
activation_params->MinDynamicObjectCount = 0;
activation_params->MaxDynamicObjectCount = 0;
activation_params->Category = AudioCategory_GameEffects;
activation_params->EventHandle = event;
activation_params->NotifyObject = NULL;
activation_params->ObjectFormat = &format;
}
typedef struct NotifyObject
{
ISpatialAudioObjectRenderStreamNotify ISpatialAudioObjectRenderStreamNotify_iface;
LONG ref;
} NotifyObject;
static WINAPI HRESULT notifyobj_QueryInterface(
ISpatialAudioObjectRenderStreamNotify *This,
REFIID riid,
void **ppvObject)
{
return S_OK;
}
static WINAPI ULONG notifyobj_AddRef(
ISpatialAudioObjectRenderStreamNotify *This)
{
NotifyObject *obj = CONTAINING_RECORD(This, NotifyObject, ISpatialAudioObjectRenderStreamNotify_iface);
ULONG ref = InterlockedIncrement(&obj->ref);
return ref;
}
static WINAPI ULONG notifyobj_Release(
ISpatialAudioObjectRenderStreamNotify *This)
{
NotifyObject *obj = CONTAINING_RECORD(This, NotifyObject, ISpatialAudioObjectRenderStreamNotify_iface);
ULONG ref = InterlockedDecrement(&obj->ref);
return ref;
}
static WINAPI HRESULT notifyobj_OnAvailableDynamicObjectCountChange(
ISpatialAudioObjectRenderStreamNotify *This,
ISpatialAudioObjectRenderStreamBase *stream,
LONGLONG deadline,
UINT32 object_count)
{
ok(FALSE, "Expected to never be notified of dynamic object count change\n");
return S_OK;
}
static const ISpatialAudioObjectRenderStreamNotifyVtbl notifyobjvtbl =
{
notifyobj_QueryInterface,
notifyobj_AddRef,
notifyobj_Release,
notifyobj_OnAvailableDynamicObjectCountChange
};
static void test_stream_activation(void)
{
HRESULT hr;
WAVEFORMATEX wrong_format;
ISpatialAudioObjectRenderStream *sas = NULL;
SpatialAudioObjectRenderStreamActivationParams activation_params;
PROPVARIANT activation_params_prop;
NotifyObject notify_object;
PropVariantInit(&activation_params_prop);
activation_params_prop.vt = VT_BLOB;
activation_params_prop.blob.cbSize = sizeof(activation_params);
activation_params_prop.blob.pBlobData = (BYTE*) &activation_params;
/* correct params */
fill_activation_params(&activation_params);
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);
ok(ISpatialAudioObjectRenderStream_Release(sas) == 0, "Expected to release the last reference\n");
/* event handle */
fill_activation_params(&activation_params);
activation_params.EventHandle = NULL;
hr = ISpatialAudioClient_ActivateSpatialAudioStream(sac, &activation_params_prop, &IID_ISpatialAudioObjectRenderStream, (void**)&sas);
ok(hr == E_INVALIDARG, "Expected lack of no EventHandle to be invalid: 0x%08lx\n", hr);
ok(sas == NULL, "Expected spatial audio stream to be set to NULL upon failed activation\n");
activation_params.EventHandle = INVALID_HANDLE_VALUE;
hr = ISpatialAudioClient_ActivateSpatialAudioStream(sac, &activation_params_prop, &IID_ISpatialAudioObjectRenderStream, (void**)&sas);
ok(hr == E_INVALIDARG, "Expected INVALID_HANDLE_VALUE to be invalid: 0x%08lx\n", hr);
ok(sas == NULL, "Expected spatial audio stream to be set to NULL upon failed activation\n");
/* must use only queried sample rate */
fill_activation_params(&activation_params);
memcpy(&wrong_format, &format, sizeof(format));
activation_params.ObjectFormat = &wrong_format;
wrong_format.nSamplesPerSec = 44100;
wrong_format.nAvgBytesPerSec = wrong_format.nSamplesPerSec * wrong_format.nBlockAlign;
hr = ISpatialAudioClient_ActivateSpatialAudioStream(sac, &activation_params_prop, &IID_ISpatialAudioObjectRenderStream, (void**)&sas);
ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT, "Expected format to be unsupported: 0x%08lx\n", hr);
ok(sas == NULL, "Expected spatial audio stream to be set to NULL upon failed activation\n");
/* dynamic objects are not supported */
if (max_dyn_count == 0)
{
fill_activation_params(&activation_params);
activation_params.StaticObjectTypeMask |= AudioObjectType_Dynamic;
hr = ISpatialAudioClient_ActivateSpatialAudioStream(sac, &activation_params_prop, &IID_ISpatialAudioObjectRenderStream, (void**)&sas);
ok(hr == E_INVALIDARG, "Expected dynamic objects type be invalid: 0x%08lx\n", hr);
ok(sas == NULL, "Expected spatial audio stream to be set to NULL upon failed activation\n");
}
activation_params.MinDynamicObjectCount = max_dyn_count + 1;
activation_params.MaxDynamicObjectCount = max_dyn_count + 1;
hr = ISpatialAudioClient_ActivateSpatialAudioStream(sac, &activation_params_prop, &IID_ISpatialAudioObjectRenderStream, (void**)&sas);
if (max_dyn_count)
ok(hr == AUDCLNT_E_UNSUPPORTED_FORMAT, "Expected dynamic object count exceeding max to be unsupported: 0x%08lx\n", hr);
else
ok(hr == E_INVALIDARG, "Expected setting dynamic object count to be invalid: 0x%08lx\n", hr);
/* ISpatialAudioObjectRenderStreamNotify */
fill_activation_params(&activation_params);
notify_object.ISpatialAudioObjectRenderStreamNotify_iface.lpVtbl = &notifyobjvtbl;
notify_object.ref = 0;
activation_params.NotifyObject = &notify_object.ISpatialAudioObjectRenderStreamNotify_iface;
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);
ok(notify_object.ref == 1, "Expected to get increased NotifyObject's ref count\n");
ok(ISpatialAudioObjectRenderStream_Release(sas) == 0, "Expected to release the last reference\n");
ok(notify_object.ref == 0, "Expected to get lowered NotifyObject's ref count\n");
}
static void test_audio_object_activation(void)
{
HRESULT hr;
BOOL is_active;
ISpatialAudioObjectRenderStream *sas = NULL;
ISpatialAudioObject *sao1, *sao2;
SpatialAudioObjectRenderStreamActivationParams activation_params;
PROPVARIANT activation_params_prop;
PropVariantInit(&activation_params_prop);
activation_params_prop.vt = VT_BLOB;
activation_params_prop.blob.cbSize = sizeof(activation_params);
activation_params_prop.blob.pBlobData = (BYTE*) &activation_params;
fill_activation_params(&activation_params);
activation_params.StaticObjectTypeMask &= ~AudioObjectType_FrontRight;
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_ActivateSpatialAudioObject(sas, AudioObjectType_FrontLeft, &sao1);
ok(hr == S_OK, "Failed to activate spatial audio object: 0x%08lx\n", hr);
hr = ISpatialAudioObject_IsActive(sao1, &is_active);
todo_wine ok(hr == S_OK, "Failed to check if spatial audio object is active: 0x%08lx\n", hr);
if (hr == S_OK)
ok(is_active, "Expected spatial audio object to be active\n");
hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_FrontLeft, &sao2);
ok(hr == SPTLAUDCLNT_E_OBJECT_ALREADY_ACTIVE, "Expected audio object to be already active: 0x%08lx\n", hr);
hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_FrontRight, &sao2);
ok(hr == SPTLAUDCLNT_E_STATIC_OBJECT_NOT_AVAILABLE, "Expected static object to be not available: 0x%08lx\n", hr);
hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_Dynamic, &sao2);
ok(hr == SPTLAUDCLNT_E_NO_MORE_OBJECTS, "Expected to not have no more dynamic objects: 0x%08lx\n", hr);
ISpatialAudioObject_Release(sao1);
ISpatialAudioObjectRenderStream_Release(sas);
}
static BOOL is_buffer_zeroed(const BYTE *buffer, UINT32 buffer_length)
{
UINT32 i;
for (i = 0; i < buffer_length; i++)
{
if (buffer[i] != 0)
return FALSE;
}
return TRUE;
}
static void test_audio_object_buffers(void)
{
UINT32 dyn_object_count, frame_count, max_frame_count, buffer_length;
SpatialAudioObjectRenderStreamActivationParams activation_params;
ISpatialAudioObjectRenderStream *sas = NULL;
PROPVARIANT activation_params_prop;
ISpatialAudioObject *sao[4];
BYTE *buffer;
INT i, j, k;
HRESULT hr;
PropVariantInit(&activation_params_prop);
activation_params_prop.vt = VT_BLOB;
activation_params_prop.blob.cbSize = sizeof(activation_params);
activation_params_prop.blob.pBlobData = (BYTE*) &activation_params;
fill_activation_params(&activation_params);
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 = ISpatialAudioClient_GetMaxFrameCount(sac, &format, &max_frame_count);
ok(hr == S_OK, "Got unexpected hr %#lx.\n", hr);
frame_count = format.nSamplesPerSec / 100; /* 10ms */
/* Most of the time the frame count matches the 10ms interval exactly.
* However (seen on some Testbot machines) it might be a bit higher for some reason. */
ok(max_frame_count <= frame_count + frame_count / 4, "Got unexpected frame count %u.\n", frame_count);
/* The tests below which check frame count from _BeginUpdatingAudioObjects fail on some Testbot machines
* with max_frame_count from _GetMaxFrameCount(). */
max_frame_count = frame_count + frame_count / 4;
hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_FrontLeft, &sao[0]);
ok(hr == S_OK, "Failed to activate spatial audio object: 0x%08lx\n", hr);
hr = ISpatialAudioObjectRenderStream_Start(sas);
ok(hr == S_OK, "Failed to activate spatial audio render stream: 0x%08lx\n", hr);
hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_FrontRight, &sao[1]);
ok(hr == S_OK, "Failed to activate spatial audio object: 0x%08lx\n", hr);
hr = WaitForSingleObject(event, 200);
ok(hr == WAIT_OBJECT_0, "Expected event to be flagged: 0x%08lx\n", hr);
hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_SideLeft, &sao[2]);
ok(hr == S_OK, "Failed to activate spatial audio object: 0x%08lx\n", hr);
hr = ISpatialAudioObjectRenderStream_BeginUpdatingAudioObjects(sas, &dyn_object_count, &frame_count);
ok(hr == S_OK, "Failed to begin updating audio objects: 0x%08lx\n", hr);
ok(dyn_object_count == 0, "Unexpected dynamic objects\n");
ok(frame_count <= max_frame_count, "Got unexpected frame count %u.\n", frame_count);
hr = ISpatialAudioObjectRenderStream_ActivateSpatialAudioObject(sas, AudioObjectType_SideRight, &sao[3]);
ok(hr == S_OK, "Failed to activate spatial audio object: 0x%08lx\n", hr);
for (i = 0; i < ARRAYSIZE(sao); i++)
{
hr = ISpatialAudioObject_GetBuffer(sao[i], &buffer, &buffer_length);
ok(hr == S_OK, "Expected to be able to get buffers for audio object: 0x%08lx\n", hr);
ok(buffer != NULL, "Expected to get a non-NULL buffer\n");
ok(buffer_length == frame_count * format.wBitsPerSample / 8, "Expected buffer length to be sample_size * frame_count = %hu but got %u\n",
frame_count * format.wBitsPerSample / 8, buffer_length);
ok(is_buffer_zeroed(buffer, buffer_length), "Expected audio object's buffer to be zeroed\n");
}
hr = ISpatialAudioObjectRenderStream_EndUpdatingAudioObjects(sas);
ok(hr == S_OK, "Failed to end updating audio objects: 0x%08lx\n", hr);
/* Emulate underrun and test frame count approximate limit. */
/* Force 1ms Sleep() timer resolution. */
timeBeginPeriod(1);
for (j = 0; j < 20; ++j)
{
hr = WaitForSingleObject(event, 200);
ok(hr == WAIT_OBJECT_0, "Expected event to be flagged: 0x%08lx, j %u.\n", hr, j);
hr = ISpatialAudioObjectRenderStream_BeginUpdatingAudioObjects(sas, &dyn_object_count, &frame_count);
ok(hr == S_OK, "Failed to begin updating audio objects: 0x%08lx\n", hr);
ok(dyn_object_count == 0, "Unexpected dynamic objects\n");
ok(frame_count <= max_frame_count, "Got unexpected frame_count %u.\n", frame_count);
/* Audio starts crackling with delays 10ms and above. However, setting such delay (that is, the delay
* which skips the whole quantum) breaks SA on some Testbot machines: _BeginUpdatingAudioObjects fails
* with SPTLAUDCLNT_E_INTERNAL starting from some iteration or WaitForSingleObject timeouts. That seems
* to work on the real hardware though. */
Sleep(5);
for (i = 0; i < ARRAYSIZE(sao); i++)
{
hr = ISpatialAudioObject_GetBuffer(sao[i], &buffer, &buffer_length);
ok(hr == S_OK, "Expected to be able to get buffers for audio object: 0x%08lx, i %d\n", hr, i);
ok(buffer != NULL, "Expected to get a non-NULL buffer\n");
ok(buffer_length == frame_count * format.wBitsPerSample / 8,
"Expected buffer length to be sample_size * frame_count = %hu but got %u\n",
frame_count * format.wBitsPerSample / 8, buffer_length);
/* Enable to hear the test sound. */
if (0)
{
if (format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT)
{
for (k = 0; k < frame_count; ++k)
{
float time_sec = 10.0f / 1000.0f * (j + (float)k / frame_count);
/* 440Hz tone. */
((float *)buffer)[k] = sinf(2.0f * M_PI * time_sec * 440.0f);
}
}
}
}
hr = ISpatialAudioObjectRenderStream_EndUpdatingAudioObjects(sas);
ok(hr == S_OK, "Failed to end updating audio objects: 0x%08lx\n", hr);
}
timeEndPeriod(1);
hr = WaitForSingleObject(event, 200);
ok(hr == WAIT_OBJECT_0, "Expected event to be flagged: 0x%08lx\n", hr);
hr = ISpatialAudioObjectRenderStream_BeginUpdatingAudioObjects(sas, &dyn_object_count, &frame_count);
ok(hr == S_OK, "Failed to begin updating audio objects: 0x%08lx\n", hr);
ok(dyn_object_count == 0, "Unexpected dynamic objects\n");
/* one more iteration but not with every object */
for (i = 0; i < ARRAYSIZE(sao) - 1; i++)
{
hr = ISpatialAudioObject_GetBuffer(sao[i], &buffer, &buffer_length);
ok(hr == S_OK, "Expected to be able to get buffers for audio object: 0x%08lx\n", hr);
ok(buffer != NULL, "Expected to get a non-NULL buffer\n");
ok(buffer_length == frame_count * format.wBitsPerSample / 8, "Expected buffer length to be sample_size * frame_count = %hu but got %u\n",
frame_count * format.wBitsPerSample / 8, buffer_length);
ok(is_buffer_zeroed(buffer, buffer_length), "Expected audio object's buffer to be zeroed\n");
}
hr = ISpatialAudioObjectRenderStream_EndUpdatingAudioObjects(sas);
ok(hr == S_OK, "Failed to end updating audio objects: 0x%08lx\n", hr);
/* ending the stream */
hr = ISpatialAudioObject_SetEndOfStream(sao[0], 0);
todo_wine ok(hr == SPTLAUDCLNT_E_OUT_OF_ORDER, "Expected that ending the stream at this point won't be allowed: 0x%08lx\n", hr);
hr = WaitForSingleObject(event, 200);
ok(hr == WAIT_OBJECT_0, "Expected event to be flagged: 0x%08lx\n", hr);
hr = ISpatialAudioObject_SetEndOfStream(sao[0], 0);
todo_wine ok(hr == SPTLAUDCLNT_E_OUT_OF_ORDER, "Expected that ending the stream at this point won't be allowed: 0x%08lx\n", hr);
hr = ISpatialAudioObjectRenderStream_BeginUpdatingAudioObjects(sas, &dyn_object_count, &frame_count);
ok(hr == S_OK, "Failed to begin updating audio objects: 0x%08lx\n", hr);
ok(dyn_object_count == 0, "Unexpected dynamic objects\n");
/* expect the object that was not updated last cycle to be invalidated */
hr = ISpatialAudioObject_GetBuffer(sao[ARRAYSIZE(sao) - 1], &buffer, &buffer_length);
todo_wine ok(hr == SPTLAUDCLNT_E_RESOURCES_INVALIDATED, "Expected audio object to be invalidated: 0x%08lx\n", hr);
for (i = 0; i < ARRAYSIZE(sao) - 1; i++)
{
hr = ISpatialAudioObject_GetBuffer(sao[i], &buffer, &buffer_length);
ok(hr == S_OK, "Expected to be able to get buffers for audio object: 0x%08lx\n", hr);
hr = ISpatialAudioObject_SetEndOfStream(sao[i], 0);
todo_wine ok(hr == S_OK, "Failed to end the stream: 0x%08lx\n", hr);
hr = ISpatialAudioObject_GetBuffer(sao[i], &buffer, &buffer_length);
todo_wine ok(hr == SPTLAUDCLNT_E_RESOURCES_INVALIDATED, "Expected audio object to be invalidated: 0x%08lx\n", hr);
}
hr = ISpatialAudioObjectRenderStream_EndUpdatingAudioObjects(sas);
ok(hr == S_OK, "Failed to end updating audio objects: 0x%08lx\n", hr);
for (i = 0; i < ARRAYSIZE(sao); i++)
{
ISpatialAudioObject_Release(sao[i]);
}
ISpatialAudioObjectRenderStream_Release(sas);
}
START_TEST(spatialaudio)
{
HRESULT hr;
event = CreateEventA(NULL, FALSE, FALSE, "spatial-audio-test-prog-event");
ok(event != NULL, "Failed to create event, last error: 0x%08lx\n", GetLastError());
CoInitializeEx(NULL, COINIT_MULTITHREADED);
hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL, CLSCTX_INPROC_SERVER, &IID_IMMDeviceEnumerator, (void**)&mme);
if (FAILED(hr))
{
skip("mmdevapi not available: 0x%08lx\n", hr);
goto cleanup;
}
hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(mme, eRender, eMultimedia, &dev);
ok(hr == S_OK || hr == E_NOTFOUND, "GetDefaultAudioEndpoint failed: 0x%08lx\n", hr);
if (hr != S_OK || !dev)
{
if (hr == E_NOTFOUND)
skip("No sound card available\n");
else
skip("GetDefaultAudioEndpoint returns 0x%08lx\n", hr);
goto cleanup;
}
hr = IMMDevice_Activate(dev, &IID_ISpatialAudioClient, CLSCTX_INPROC_SERVER, NULL, (void**)&sac);
ok(hr == S_OK || hr == E_NOINTERFACE, "ISpatialAudioClient Activation failed: 0x%08lx\n", hr);
if (hr != S_OK || !dev)
{
if (hr == E_NOINTERFACE)
skip("ISpatialAudioClient interface not found\n");
else
skip("ISpatialAudioClient Activation returns 0x%08lx\n", hr);
goto cleanup;
}
hr = ISpatialAudioClient_GetMaxDynamicObjectCount(sac, &max_dyn_count);
ok(hr == S_OK, "Failed to get max dynamic object count: 0x%08lx\n", hr);
/* that's the default, after manually enabling Windows Sonic it's possible to have max_dyn_count > 0 */
/* ok(max_dyn_count == 0, "expected max dynamic object count to be 0 got %u\n", max_dyn_count); */
test_formats();
test_stream_activation();
test_audio_object_activation();
test_audio_object_buffers();
ISpatialAudioClient_Release(sac);
cleanup:
if (dev)
IMMDevice_Release(dev);
if (mme)
IMMDeviceEnumerator_Release(mme);
CoUninitialize();
CloseHandle(event);
}

View file

@ -0,0 +1,336 @@
/*
* Copyright 2021 Jacek Caban for CodeWeavers
* Copyright 2021-2022 Huw Davies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "audioclient.h"
#include "mmdeviceapi.h"
typedef UINT64 stream_handle;
enum driver_priority
{
Priority_Unavailable = 0, /* driver won't work */
Priority_Low, /* driver may work, but unlikely */
Priority_Neutral, /* driver makes no judgment */
Priority_Preferred /* driver thinks it's correct */
};
struct endpoint
{
unsigned int name;
unsigned int device;
};
struct main_loop_params
{
HANDLE event;
};
struct get_endpoint_ids_params
{
EDataFlow flow;
struct endpoint *endpoints;
unsigned int size;
HRESULT result;
unsigned int num;
unsigned int default_idx;
};
struct create_stream_params
{
const char *name;
const char *device;
EDataFlow flow;
AUDCLNT_SHAREMODE share;
DWORD flags;
REFERENCE_TIME duration;
REFERENCE_TIME period;
const WAVEFORMATEX *fmt;
HRESULT result;
UINT32 *channel_count;
stream_handle *stream;
};
struct release_stream_params
{
stream_handle stream;
HANDLE timer_thread;
HRESULT result;
};
struct start_params
{
stream_handle stream;
HRESULT result;
};
struct stop_params
{
stream_handle stream;
HRESULT result;
};
struct reset_params
{
stream_handle stream;
HRESULT result;
};
struct timer_loop_params
{
stream_handle stream;
};
struct get_render_buffer_params
{
stream_handle stream;
UINT32 frames;
HRESULT result;
BYTE **data;
};
struct release_render_buffer_params
{
stream_handle stream;
UINT32 written_frames;
UINT flags;
HRESULT result;
};
struct get_capture_buffer_params
{
stream_handle stream;
HRESULT result;
BYTE **data;
UINT32 *frames;
UINT *flags;
UINT64 *devpos;
UINT64 *qpcpos;
};
struct release_capture_buffer_params
{
stream_handle stream;
UINT32 done;
HRESULT result;
};
struct is_format_supported_params
{
const char *device;
EDataFlow flow;
AUDCLNT_SHAREMODE share;
const WAVEFORMATEX *fmt_in;
WAVEFORMATEXTENSIBLE *fmt_out;
HRESULT result;
};
struct get_mix_format_params
{
const char *device;
EDataFlow flow;
WAVEFORMATEXTENSIBLE *fmt;
HRESULT result;
};
struct get_device_period_params
{
const char *device;
EDataFlow flow;
HRESULT result;
REFERENCE_TIME *def_period;
REFERENCE_TIME *min_period;
};
struct get_buffer_size_params
{
stream_handle stream;
HRESULT result;
UINT32 *frames;
};
struct get_latency_params
{
stream_handle stream;
HRESULT result;
REFERENCE_TIME *latency;
};
struct get_current_padding_params
{
stream_handle stream;
HRESULT result;
UINT32 *padding;
};
struct get_next_packet_size_params
{
stream_handle stream;
HRESULT result;
UINT32 *frames;
};
struct get_frequency_params
{
stream_handle stream;
HRESULT result;
UINT64 *freq;
};
struct get_position_params
{
stream_handle stream;
BOOL device;
HRESULT result;
UINT64 *pos;
UINT64 *qpctime;
};
struct set_volumes_params
{
stream_handle stream;
float master_volume;
const float *volumes;
const float *session_volumes;
int channel;
};
struct set_event_handle_params
{
stream_handle stream;
HANDLE event;
HRESULT result;
};
struct test_connect_params
{
const char *name;
enum driver_priority priority;
};
struct is_started_params
{
stream_handle stream;
HRESULT result;
};
struct get_prop_value_params
{
const char *device;
EDataFlow flow;
const GUID *guid;
const PROPERTYKEY *prop;
HRESULT result;
PROPVARIANT *value;
void *buffer; /* caller allocated buffer to hold value's strings */
unsigned int *buffer_size;
};
struct midi_init_params
{
UINT *err;
};
struct notify_context
{
BOOL send_notify;
WORD dev_id;
WORD msg;
UINT_PTR param_1;
UINT_PTR param_2;
UINT_PTR callback;
UINT flags;
HANDLE device;
UINT_PTR instance;
};
struct midi_out_message_params
{
UINT dev_id;
UINT msg;
UINT_PTR user;
UINT_PTR param_1;
UINT_PTR param_2;
UINT *err;
struct notify_context *notify;
};
struct midi_in_message_params
{
UINT dev_id;
UINT msg;
UINT_PTR user;
UINT_PTR param_1;
UINT_PTR param_2;
UINT *err;
struct notify_context *notify;
};
struct midi_notify_wait_params
{
BOOL *quit;
struct notify_context *notify;
};
struct aux_message_params
{
UINT dev_id;
UINT msg;
UINT_PTR user;
UINT_PTR param_1;
UINT_PTR param_2;
UINT *err;
};
enum unix_funcs
{
process_attach,
process_detach,
main_loop,
get_endpoint_ids,
create_stream,
release_stream,
start,
stop,
reset,
timer_loop,
get_render_buffer,
release_render_buffer,
get_capture_buffer,
release_capture_buffer,
is_format_supported,
get_mix_format,
get_device_period,
get_buffer_size,
get_latency,
get_current_padding,
get_next_packet_size,
get_frequency,
get_position,
set_volumes,
set_event_handle,
test_connect,
is_started,
get_prop_value,
midi_init,
midi_release,
midi_out_message,
midi_in_message,
midi_notify_wait,
aux_message,
};

View file

@ -0,0 +1,11 @@
MODULE = winealsa.drv
UNIXLIB = winealsa.so
IMPORTS = uuid ole32 advapi32
DELAYIMPORTS = winmm
UNIX_LIBS = $(ALSA_LIBS) $(PTHREAD_LIBS)
SOURCES = \
alsa.c \
alsamidi.c \
midi.c \
mmdevdrv.c

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,157 @@
/*
* MIDI driver for ALSA (PE-side)
*
* Copyright 1994 Martin Ayotte
* Copyright 1998 Luiz Otavio L. Zorzella
* Copyright 1998, 1999 Eric POUECH
* Copyright 2003 Christian Costa
* Copyright 2022 Huw Davies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winternl.h"
#include "mmddk.h"
#include "mmdeviceapi.h"
#include "wine/debug.h"
#include "wine/unixlib.h"
#include "unixlib.h"
WINE_DEFAULT_DEBUG_CHANNEL(midi);
static void notify_client(struct notify_context *notify)
{
TRACE("dev_id = %d msg = %d param1 = %04IX param2 = %04IX\n", notify->dev_id, notify->msg, notify->param_1, notify->param_2);
DriverCallback(notify->callback, notify->flags, notify->device, notify->msg,
notify->instance, notify->param_1, notify->param_2);
}
/*======================================================================*
* MIDI entry points *
*======================================================================*/
/**************************************************************************
* midMessage (WINEALSA.@)
*/
DWORD WINAPI ALSA_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
struct midi_in_message_params params;
struct notify_context notify;
UINT err;
TRACE("(%04X, %04X, %08IX, %08IX, %08IX);\n",
wDevID, wMsg, dwUser, dwParam1, dwParam2);
params.dev_id = wDevID;
params.msg = wMsg;
params.user = dwUser;
params.param_1 = dwParam1;
params.param_2 = dwParam2;
params.err = &err;
params.notify = &notify;
do
{
ALSA_CALL(midi_in_message, &params);
if ((!err || err == ERROR_RETRY) && notify.send_notify) notify_client(&notify);
} while (err == ERROR_RETRY);
return err;
}
/**************************************************************************
* modMessage (WINEALSA.@)
*/
DWORD WINAPI ALSA_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
struct midi_out_message_params params;
struct notify_context notify;
UINT err;
TRACE("(%04X, %04X, %08IX, %08IX, %08IX);\n",
wDevID, wMsg, dwUser, dwParam1, dwParam2);
params.dev_id = wDevID;
params.msg = wMsg;
params.user = dwUser;
params.param_1 = dwParam1;
params.param_2 = dwParam2;
params.err = &err;
params.notify = &notify;
ALSA_CALL(midi_out_message, &params);
if (!err && notify.send_notify) notify_client(&notify);
return err;
}
static DWORD WINAPI notify_thread(void *p)
{
struct midi_notify_wait_params params;
struct notify_context notify;
BOOL quit;
SetThreadDescription(GetCurrentThread(), L"winealsa_midi_notify");
params.notify = &notify;
params.quit = &quit;
while (1)
{
ALSA_CALL(midi_notify_wait, &params);
if (quit) break;
if (notify.send_notify) notify_client(&notify);
}
return 0;
}
/**************************************************************************
* DriverProc (WINEALSA.@)
*/
LRESULT CALLBACK ALSA_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
LPARAM dwParam1, LPARAM dwParam2)
{
switch(wMsg) {
case DRV_LOAD:
CloseHandle(CreateThread(NULL, 0, notify_thread, NULL, 0, NULL));
return 1;
case DRV_FREE:
ALSA_CALL(midi_release, NULL);
return 1;
case DRV_OPEN:
case DRV_CLOSE:
case DRV_ENABLE:
case DRV_DISABLE:
case DRV_QUERYCONFIGURE:
case DRV_CONFIGURE:
return 1;
case DRV_INSTALL:
case DRV_REMOVE:
return DRV_SUCCESS;
default:
return 0;
}
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,32 @@
/*
* Copyright 2022 Huw Davies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "../mmdevapi/unixlib.h"
NTSTATUS alsa_midi_release(void *args);
NTSTATUS alsa_midi_out_message(void *args);
NTSTATUS alsa_midi_in_message(void *args);
NTSTATUS alsa_midi_notify_wait(void *args);
#ifdef _WIN64
NTSTATUS alsa_wow64_midi_out_message(void *args);
NTSTATUS alsa_wow64_midi_in_message(void *args);
NTSTATUS alsa_wow64_midi_notify_wait(void *args);
#endif
#define ALSA_CALL(func, params) WINE_UNIX_CALL(func, params)

View file

@ -0,0 +1,11 @@
# WinMM driver functions
@ stdcall -private DriverProc(long long long long long) ALSA_DriverProc
@ stdcall -private midMessage(long long long long long) ALSA_midMessage
@ stdcall -private modMessage(long long long long long) ALSA_modMessage
# MMDevAPI driver functions
@ stdcall -private GetPriority() AUDDRV_GetPriority
@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint
@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
@ stdcall -private GetPropValue(ptr ptr ptr) AUDDRV_GetPropValue

View file

@ -0,0 +1,11 @@
MODULE = winecoreaudio.drv
UNIXLIB = winecoreaudio.so
IMPORTS = uuid ole32 user32 advapi32
DELAYIMPORTS = winmm
UNIX_LIBS = $(COREAUDIO_LIBS)
SOURCES = \
coreaudio.c \
coremidi.c \
midi.c \
mmdevdrv.c

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
/* Definition for CoreAudio drivers : wine multimedia system
*
* Copyright 2005-2007 Emmanuel Maillard
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef __WINE_COREAUDIO_H
#define __WINE_COREAUDIO_H
#include "wine/debug.h"
/* fourcc is in native order, where MSB is the first character. */
static inline const char* wine_dbgstr_fourcc(INT32 fourcc)
{
char buf[4] = { (char) (fourcc >> 24), (char) (fourcc >> 16),
(char) (fourcc >> 8), (char) fourcc };
/* OSStatus is a signed decimal except in parts of CoreAudio */
if ((buf[0] < 32) || (buf[1] < 32) || (buf[2] < 32) || (buf[3] < 32))
return wine_dbg_sprintf("%d", fourcc);
return wine_dbgstr_an(buf, sizeof(buf));
}
#endif /* __WINE_COREAUDIO_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,226 @@
/*
* MIDI driver for macOS (PE-side)
*
* Copyright 1994 Martin Ayotte
* Copyright 1998 Luiz Otavio L. Zorzella
* Copyright 1998, 1999 Eric POUECH
* Copyright 2005, 2006 Emmanuel Maillard
* Copyright 2021 Huw Davies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include "windef.h"
#include "winbase.h"
#include "winternl.h"
#include "wingdi.h"
#include "winuser.h"
#include "winnls.h"
#include "mmddk.h"
#include "mmdeviceapi.h"
#include "audioclient.h"
#include "wine/debug.h"
#include "wine/unixlib.h"
#include "coreaudio.h"
#include "unixlib.h"
WINE_DEFAULT_DEBUG_CHANNEL(midi);
static void notify_client(struct notify_context *notify)
{
TRACE("dev_id=%d msg=%d param1=%04IX param2=%04IX\n", notify->dev_id, notify->msg, notify->param_1, notify->param_2);
DriverCallback(notify->callback, notify->flags, notify->device, notify->msg,
notify->instance, notify->param_1, notify->param_2);
}
static DWORD WINAPI notify_thread(void *p)
{
struct midi_notify_wait_params params;
struct notify_context notify;
BOOL quit;
params.notify = &notify;
params.quit = &quit;
while (1)
{
UNIX_CALL(midi_notify_wait, &params);
if (quit) break;
if (notify.send_notify) notify_client(&notify);
}
return 0;
}
static LONG CoreAudio_MIDIInit(void)
{
struct midi_init_params params;
UINT err;
params.err = &err;
UNIX_CALL(midi_init, &params);
if (err != DRV_SUCCESS)
{
ERR("can't create midi client\n");
return err;
}
CloseHandle(CreateThread(NULL, 0, notify_thread, NULL, 0, NULL));
return err;
}
static LONG CoreAudio_MIDIRelease(void)
{
TRACE("\n");
UNIX_CALL(midi_release, NULL);
return DRV_SUCCESS;
}
/**************************************************************************
* modMessage
*/
DWORD WINAPI CoreAudio_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
struct midi_out_message_params params;
struct notify_context notify;
UINT err;
TRACE("%d %08x %08Ix %08Ix %08Ix\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
params.dev_id = wDevID;
params.msg = wMsg;
params.user = dwUser;
params.param_1 = dwParam1;
params.param_2 = dwParam2;
params.err = &err;
params.notify = &notify;
UNIX_CALL(midi_out_message, &params);
if (!err && notify.send_notify) notify_client(&notify);
return err;
}
/**************************************************************************
* midMessage
*/
DWORD WINAPI CoreAudio_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser, DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
struct midi_in_message_params params;
struct notify_context notify;
UINT err;
TRACE("%d %08x %08Ix %08Ix %08Ix\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
params.dev_id = wDevID;
params.msg = wMsg;
params.user = dwUser;
params.param_1 = dwParam1;
params.param_2 = dwParam2;
params.err = &err;
params.notify = &notify;
do
{
UNIX_CALL(midi_in_message, &params);
if ((!err || err == ERROR_RETRY) && notify.send_notify) notify_client(&notify);
} while (err == ERROR_RETRY);
return err;
}
/**************************************************************************
* CoreAudio_drvLoad [internal]
*/
static LRESULT CoreAudio_drvLoad(void)
{
TRACE("()\n");
if (CoreAudio_MIDIInit() != DRV_SUCCESS)
return DRV_FAILURE;
return DRV_SUCCESS;
}
/**************************************************************************
* CoreAudio_drvFree [internal]
*/
static LRESULT CoreAudio_drvFree(void)
{
TRACE("()\n");
CoreAudio_MIDIRelease();
return DRV_SUCCESS;
}
/**************************************************************************
* CoreAudio_drvOpen [internal]
*/
static LRESULT CoreAudio_drvOpen(LPSTR str)
{
TRACE("(%s)\n", str);
return 1;
}
/**************************************************************************
* CoreAudio_drvClose [internal]
*/
static DWORD CoreAudio_drvClose(DWORD dwDevID)
{
TRACE("(%08lx)\n", dwDevID);
return 1;
}
/**************************************************************************
* DriverProc (WINECOREAUDIO.1)
*/
LRESULT CALLBACK CoreAudio_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
LPARAM dwParam1, LPARAM dwParam2)
{
TRACE("(%08IX, %p, %s (%08X), %08IX, %08IX)\n",
dwDevID, hDriv, wMsg == DRV_LOAD ? "DRV_LOAD" :
wMsg == DRV_FREE ? "DRV_FREE" :
wMsg == DRV_OPEN ? "DRV_OPEN" :
wMsg == DRV_CLOSE ? "DRV_CLOSE" :
wMsg == DRV_ENABLE ? "DRV_ENABLE" :
wMsg == DRV_DISABLE ? "DRV_DISABLE" :
wMsg == DRV_QUERYCONFIGURE ? "DRV_QUERYCONFIGURE" :
wMsg == DRV_CONFIGURE ? "DRV_CONFIGURE" :
wMsg == DRV_INSTALL ? "DRV_INSTALL" :
wMsg == DRV_REMOVE ? "DRV_REMOVE" : "UNKNOWN",
wMsg, dwParam1, dwParam2);
switch(wMsg) {
case DRV_LOAD: return CoreAudio_drvLoad();
case DRV_FREE: return CoreAudio_drvFree();
case DRV_OPEN: return CoreAudio_drvOpen((LPSTR)dwParam1);
case DRV_CLOSE: return CoreAudio_drvClose(dwDevID);
case DRV_ENABLE: return 1;
case DRV_DISABLE: return 1;
case DRV_QUERYCONFIGURE: return 1;
case DRV_CONFIGURE: MessageBoxA(0, "CoreAudio driver!", "CoreAudio driver", MB_OK); return 1;
case DRV_INSTALL: return DRVCNF_RESTART;
case DRV_REMOVE: return DRVCNF_RESTART;
default:
return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,36 @@
/*
* Unixlib header file for winecoreaudio driver.
*
* Copyright 2021 Huw Davies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "../mmdevapi/unixlib.h"
NTSTATUS unix_midi_init( void * );
NTSTATUS unix_midi_release( void * );
NTSTATUS unix_midi_out_message( void * );
NTSTATUS unix_midi_in_message( void * );
NTSTATUS unix_midi_notify_wait( void * );
#ifdef _WIN64
NTSTATUS unix_wow64_midi_init(void *args);
NTSTATUS unix_wow64_midi_out_message(void *args);
NTSTATUS unix_wow64_midi_in_message(void *args);
NTSTATUS unix_wow64_midi_notify_wait(void *args);
#endif
#define UNIX_CALL( func, params ) WINE_UNIX_CALL( func, params )

View file

@ -0,0 +1,10 @@
# WinMM driver functions
@ stdcall -private DriverProc(long long long long long) CoreAudio_DriverProc
@ stdcall -private midMessage(long long long long long) CoreAudio_midMessage
@ stdcall -private modMessage(long long long long long) CoreAudio_modMessage
# MMDevAPI driver functions
@ stdcall -private GetPriority() AUDDRV_GetPriority
@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint
@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager

View file

@ -0,0 +1,14 @@
MODULE = wineoss.drv
UNIXLIB = wineoss.so
IMPORTS = uuid ole32 user32 advapi32
DELAYIMPORTS = winmm
UNIX_LIBS = $(OSS4_LIBS) $(PTHREAD_LIBS)
UNIX_CFLAGS = $(OSS4_CFLAGS)
SOURCES = \
midi.c \
midipatch.c \
mmaux.c \
mmdevdrv.c \
oss.c \
ossmidi.c

View file

@ -0,0 +1,176 @@
/*
* MIDI driver for OSS (PE-side)
*
* Copyright 1994 Martin Ayotte
* Copyright 1998 Luiz Otavio L. Zorzella (init procedures)
* Copyright 1998, 1999 Eric POUECH
* Copyright 2022 Huw Davies
*
* 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
*/
/* TODO:
* + use better instrument definition for OPL/2 (midiPatch.c) or
* use existing instrument definition (from playmidi or kmid)
* with a .winerc option
* + have a look at OPL/3 ?
* + implement asynchronous playback of MidiHdr
* + implement STREAM'ed MidiHdr (question: how shall we share the
* code between the midiStream functions in MMSYSTEM/WINMM and
* the code for the low level driver)
* + use a more accurate read mechanism than the one of snooping on
* timers (like select on fd)
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "winternl.h"
#include "mmddk.h"
#include "audioclient.h"
#include "wine/debug.h"
#include "wine/unixlib.h"
#include "unixlib.h"
WINE_DEFAULT_DEBUG_CHANNEL(midi);
/*======================================================================*
* Low level MIDI implementation *
*======================================================================*/
static void notify_client(struct notify_context *notify)
{
TRACE("dev_id = %d msg = %d param1 = %04IX param2 = %04IX\n",
notify->dev_id, notify->msg, notify->param_1, notify->param_2);
DriverCallback(notify->callback, notify->flags, notify->device, notify->msg,
notify->instance, notify->param_1, notify->param_2);
}
/*======================================================================*
* MIDI entry points *
*======================================================================*/
/**************************************************************************
* midMessage (WINEOSS.@)
*/
DWORD WINAPI OSS_midMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
struct midi_in_message_params params;
struct notify_context notify;
UINT err;
TRACE("(%04X, %04X, %08IX, %08IX, %08IX);\n",
wDevID, wMsg, dwUser, dwParam1, dwParam2);
params.dev_id = wDevID;
params.msg = wMsg;
params.user = dwUser;
params.param_1 = dwParam1;
params.param_2 = dwParam2;
params.err = &err;
params.notify = &notify;
do
{
OSS_CALL(midi_in_message, &params);
if ((!err || err == ERROR_RETRY) && notify.send_notify) notify_client(&notify);
} while (err == ERROR_RETRY);
return err;
}
/**************************************************************************
* modMessage (WINEOSS.@)
*/
DWORD WINAPI OSS_modMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
struct midi_out_message_params params;
struct notify_context notify;
UINT err;
TRACE("(%04X, %04X, %08IX, %08IX, %08IX);\n",
wDevID, wMsg, dwUser, dwParam1, dwParam2);
params.dev_id = wDevID;
params.msg = wMsg;
params.user = dwUser;
params.param_1 = dwParam1;
params.param_2 = dwParam2;
params.err = &err;
params.notify = &notify;
OSS_CALL(midi_out_message, &params);
if (!err && notify.send_notify) notify_client(&notify);
return err;
}
static DWORD WINAPI notify_thread(void *p)
{
struct midi_notify_wait_params params;
struct notify_context notify;
BOOL quit;
params.notify = &notify;
params.quit = &quit;
while (1)
{
OSS_CALL(midi_notify_wait, &params);
if (quit) break;
if (notify.send_notify) notify_client(&notify);
}
return 0;
}
/**************************************************************************
* DriverProc (WINEOSS.1)
*/
LRESULT CALLBACK OSS_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
LPARAM dwParam1, LPARAM dwParam2)
{
TRACE("(%08IX, %p, %08X, %08IX, %08IX)\n",
dwDevID, hDriv, wMsg, dwParam1, dwParam2);
switch(wMsg) {
case DRV_LOAD:
CloseHandle(CreateThread(NULL, 0, notify_thread, NULL, 0, NULL));
return 1;
case DRV_FREE:
OSS_CALL(midi_release, NULL);
return 1;
case DRV_OPEN:
case DRV_CLOSE:
case DRV_ENABLE:
case DRV_DISABLE:
case DRV_QUERYCONFIGURE:
case DRV_CONFIGURE:
return 1;
case DRV_INSTALL:
case DRV_REMOVE:
return DRV_SUCCESS;
default:
return 0;
}
}

View file

@ -0,0 +1,293 @@
/* -*- tab-width: 8; c-basic-offset: 4 -*- */
/*
* FM patches for wine MIDI driver
*
* Copyright 1999 Eric Pouech
*
* 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
*/
/*
* Eric POUECH : MIDI FM patches for GM instruments
*/
#if 0
#pragma makedep unix
#endif
#define NOT_DEFINED 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
const unsigned char midiFMInstrumentPatches[128 * 16] = {
/* 0 Acoustic Grand Piano */ 0x21, 0x11, 0x4c, 0x00, 0xf1, 0xf2, 0x63, 0x72, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 1 Bright Acoustic Piano */ 0x01, 0x11, 0x4f, 0x00, 0xf1, 0xd2, 0x53, 0x74, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 2 Electric Grand Piano */ 0x01, 0x01, 0x4f, 0x04, 0xf1, 0xd2, 0x50, 0x7c, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 3 Honky-Tonk Piano */ 0x81, 0x13, 0x9d, 0x00, 0xf2, 0xf2, 0x51, 0xf1, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 4 Rhodes Piano */ 0x01, 0x01, 0x4f, 0x04, 0xf1, 0xd2, 0x50, 0x7c, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 5 Chorused Piano */ 0x01, 0x11, 0x4d, 0x00, 0xf1, 0xd2, 0x60, 0x7b, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 6 Harpsichord */ 0x32, 0x16, 0x87, 0x80, 0xa1, 0x7d, 0x10, 0x33, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 7 Clavinet */ 0x13, 0x08, 0x80, 0x00, 0xfb, 0xe8, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 8 Celesta */ 0x14, 0x04, 0x07, 0x00, 0x93, 0xb6, 0x73, 0x62, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 9 Glockenspiel */ 0x07, 0x12, 0x4f, 0x00, 0xf2, 0xf2, 0x60, 0x72, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 10 Music Box */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 11 Vibraphone */ 0x44, 0x60, 0x53, 0x80, 0xf5, 0xfd, 0x33, 0x25, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 12 Marimba */ 0x05, 0x01, 0x4e, 0x00, 0xda, 0xf9, 0x25, 0x15, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 13 Xylophone */ 0x11, 0x31, 0x2d, 0x00, 0xc8, 0xf5, 0x2f, 0xf5, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 14 Tubular Bells */ 0x03, 0x17, 0x4f, 0x03, 0xf1, 0xf2, 0x53, 0x74, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 15 Dulcimer */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 16 Hammond Organ */ 0x72, 0x71, 0xcd, 0x80, 0x91, 0x91, 0x2a, 0x2a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 17 Percussive Organ */ 0x0c, 0x00, 0x00, 0x00, 0xf8, 0xd6, 0xb5, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 18 Rock Organ */ 0x72, 0x70, 0xce, 0x80, 0x9f, 0x94, 0x12, 0x11, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 19 Church Organ */ 0xa5, 0xb1, 0xd2, 0x80, 0x81, 0xf1, 0x03, 0x05, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 20 Reed Organ */ 0x3e, 0xb1, 0x29, 0x80, 0xfb, 0xa0, 0xf0, 0x9b, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 21 Accordion */ 0x24, 0x31, 0x4f, 0x00, 0xf2, 0x52, 0x0b, 0x0b, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 22 Harmonica */ 0x22, 0xf2, 0x8f, 0x40, 0x41, 0x61, 0x03, 0x05, 0x02, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 23 Tango Accordion */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 24 Acoustic Nylon Guitar */ 0x01, 0x01, 0x11, 0x00, 0xf2, 0xf5, 0x1f, 0x88, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 25 Acoustic Steel Guitar */ 0x01, 0xa1, 0x46, 0x03, 0xf1, 0x31, 0x83, 0x86, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 26 Electric Jazz Guitar */ 0x03, 0x11, 0x5e, 0x00, 0x85, 0xd2, 0x51, 0x71, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 27 Electric Clean Guitar */ 0x32, 0x16, 0x87, 0x80, 0xa1, 0x7d, 0x10, 0x33, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 28 Electric Muted Guitar */ 0x13, 0x11, 0x96, 0x80, 0xff, 0xff, 0x21, 0x03, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 29 Overdriven Guitar */ 0x07, 0x14, 0x8f, 0x80, 0x82, 0x82, 0x7d, 0x7d, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 30 Distortion Guitar */ 0x05, 0x01, 0x8f, 0x80, 0xda, 0xf9, 0x15, 0x14, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 31 Guitar Harmonics */ 0xc3, 0x01, 0x05, 0x0d, 0x91, 0xf1, 0x1f, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 32 Acoustic Bass */ 0x21, 0x01, 0x2a, 0x00, 0xf2, 0xf5, 0x1f, 0x88, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 33 Electric Bass Fingered */ 0x01, 0x21, 0x15, 0x80, 0x25, 0x65, 0x2f, 0x6c, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 34 Electric Bass Picked */ 0x01, 0x01, 0x1d, 0x00, 0xf2, 0xf5, 0xef, 0x78, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 35 Fretless Bass */ 0x30, 0x21, 0x1e, 0x00, 0xf2, 0xf5, 0xef, 0x78, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 36 Slap Bass 1 */ 0x20, 0x21, 0x40, 0x00, 0x7b, 0x75, 0x04, 0x72, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 37 Slap Bass 2 */ 0x20, 0x21, 0x40, 0x00, 0x7b, 0xf5, 0x04, 0x72, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 38 Synth Bass 1 */ 0x41, 0x91, 0x83, 0x00, 0x65, 0x32, 0x05, 0x74, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 39 Synth Bass 2 */ 0x30, 0xb1, 0x88, 0x80, 0xd5, 0x61, 0x19, 0x1b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 40 Violin */ 0x72, 0x62, 0x1c, 0x05, 0x51, 0x52, 0x03, 0x13, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 41 Viola */ 0x70, 0x71, 0xd0, 0x80, 0x52, 0x31, 0x11, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 42 Cello */ 0x70, 0x71, 0xc5, 0x80, 0x52, 0x31, 0x11, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 43 Contrabass */ 0x01, 0x00, 0x00, 0x00, 0x94, 0x83, 0xb6, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 44 Tremolo Strings */ 0x71, 0xa1, 0x8b, 0x40, 0x71, 0x42, 0x11, 0x15, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 45 Pizzicato Strings */ 0xf2, 0xe1, 0x40, 0x80, 0xf5, 0xfd, 0xa8, 0xad, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 46 Orchestral Harp */ 0x21, 0x11, 0x11, 0x00, 0xa3, 0xc4, 0x43, 0x22, 0x02, 0x00, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 47 Timpani */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 48 String Ensemble 1 */ 0xe1, 0x21, 0x4f, 0x00, 0xc1, 0x32, 0xd3, 0x74, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 49 String Ensemble 2 */ 0xe1, 0x21, 0x4f, 0x00, 0xb1, 0x12, 0xd3, 0x74, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 50 Synth Strings 1 */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 51 Synth Strings 2 */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 52 Choir Aahs */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 53 Voice oohs */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 54 Synth Voice */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 55 Orchestra Hit */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 56 Trumpet */ 0x31, 0xa1, 0x1c, 0x80, 0x41, 0x92, 0x0b, 0x3b, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 57 Trombone */ 0x21, 0xa1, 0x18, 0x80, 0x53, 0x52, 0x1d, 0x3b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 58 Tuba */ 0x21, 0x21, 0x19, 0x80, 0x43, 0x85, 0x8c, 0x2f, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 59 Muted Trumpet */ 0x31, 0xa1, 0x1c, 0x80, 0x41, 0x92, 0x0b, 0x3b, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 60 French Horn */ 0x21, 0x21, 0x9f, 0x80, 0x53, 0xaa, 0x5a, 0x1a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 61 Brass Section */ 0x21, 0x21, 0x16, 0x00, 0x71, 0x81, 0xae, 0x9e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 62 Synth Brass 1 */ 0x61, 0x60, 0x1c, 0x00, 0x71, 0x81, 0xae, 0x2e, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 63 Synth Brass 2 */ 0x21, 0x21, 0x8e, 0x80, 0xbb, 0x90, 0x29, 0x0a, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 64 Soprano Sax */ 0x01, 0x12, 0x4f, 0x00, 0x71, 0x52, 0x53, 0x7c, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 65 Alto Sax */ 0x01, 0x13, 0x4f, 0x00, 0x71, 0x62, 0x53, 0x84, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 66 Tenor Sax */ 0x01, 0x13, 0x8d, 0x00, 0x51, 0x52, 0x53, 0x7c, 0x01, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 67 Baritone Sax */ 0x01, 0x12, 0x4f, 0x00, 0x71, 0x22, 0x53, 0x7c, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 68 Oboe */ 0x71, 0x62, 0xc5, 0x05, 0x6e, 0x8b, 0x17, 0x0e, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 69 English Horn */ 0xe1, 0xe4, 0x23, 0x00, 0x71, 0x82, 0xae, 0x9e, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 70 Bassoon */ 0x30, 0xb1, 0xcd, 0x80, 0xd5, 0x61, 0x19, 0x1b, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 71 Clarinet */ 0x32, 0xa1, 0x1c, 0x80, 0x51, 0x82, 0x15, 0x45, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 72 Piccolo */ 0xe4, 0xe4, 0x0f, 0x00, 0x70, 0x60, 0x0f, 0x9f, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 73 Flute */ 0xe1, 0x61, 0x27, 0x80, 0x53, 0x53, 0x8a, 0x57, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 74 Recorder */ 0x61, 0x61, 0x27, 0x80, 0x74, 0x65, 0x8f, 0x2a, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 75 Pan Flute */ 0xe0, 0xa1, 0xec, 0x00, 0x6e, 0x65, 0x8f, 0x2a, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 76 Bottle Blow */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 77 Shakuhashi */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 78 Whistle */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 79 Ocarina */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 80 Synth lead 1 - Sq wave lead */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 81 Synth lead 2 - Sawtooth Wave */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 82 Synth lead 3 - Caliope lead */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 83 Synth lead 4 - Chiff lead */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 84 Synth lead 5 - Charang */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 85 Synth lead 6 - Solo Synth Voice */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 86 Synth lead 7 - Bright Saw Wave */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 87 Synth lead 8 - Brass and Lead */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 88 Synth pad 1 - Fantasia Pad */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 89 Synth pad 2 - Warm Pad */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 90 Synth pad 3 - Poly Synth Pad */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 91 Synth pad 4 - Space Voice Pad */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 92 Synth pad 5 - Bowed Glass Pad */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 93 Synth pad 6 - Metal Pad */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 94 Synth pad 7 - Halo Pad */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 95 Synth pad 8 - Sweep Pad */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 96 Synth SFX 1 - Ice Rain */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 97 Synth SFX 2 - Soundtrack */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 98 Synth SFX 3 - Crystal */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 99 Synth SFX 4 - Atmosphere */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 100 Synth SFX 5 - Brightness */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 101 Synth SFX 6 - Goblin */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 102 Synth SFX 7 - Echo drops */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 103 Synth SFX 8 - Star Theme */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 104 Sitar */ 0x01, 0x08, 0x40, 0x00, 0xf2, 0xf2, 0x54, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 105 Banjo */ 0x31, 0x16, 0x87, 0x80, 0xa1, 0x7d, 0x11, 0x43, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 106 Shamisen */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 107 Koto */ 0x0e, 0x02, 0x40, 0x00, 0x09, 0xf7, 0x53, 0x94, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 108 Kalimba */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 109 Bagpipe */ 0x31, 0x22, 0x43, 0x05, 0x6e, 0x8b, 0x17, 0x0c, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 110 Fiddle */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 111 Shanai */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 112 Tinkle Bell */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 113 Agogo */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 114 Steel Drums */ 0x00, 0x00, 0x0b, 0x00, 0xa8, 0xd6, 0x4c, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 115 Woodblock */ 0x02, 0x11, 0x4f, 0x00, 0x71, 0x52, 0x53, 0x7c, 0x00, 0x00, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 116 Taiko Drum */ 0x12, 0x02, 0x0b, 0x00, 0x95, 0xd4, 0x4c, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 117 Melodic Tom */ 0x01, 0x02, 0x00, 0x00, 0xfa, 0xda, 0xbf, 0xbf, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 118 Synth Drum */ 0x06, 0x00, 0x00, 0x00, 0xf0, 0xf6, 0xf0, 0xb4, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 119 Reverse Cymbal */ 0x64, 0x03, 0x00, 0x40, 0xb2, 0x97, 0x82, 0xd4, 0x02, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 120 Guitar Fret Noise */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 121 Breath Noise */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 122 Seashore */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 123 Bird Tweet */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 124 Telephone Ring */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 125 Helicopter */ 0xf0, 0xe2, 0x00, 0xc0, 0x1e, 0x11, 0x11, 0x11, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 126 Applause */ 0x07, 0x01, 0x87, 0x80, 0xf0, 0xf0, 0x05, 0x05, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 127 Gunshot */ 0x0c, 0x50, 0x00, 0x21, 0xf8, 0x09, 0xb6, 0x04, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
};
const unsigned char midiFMDrumsPatches[16 * 128] = {
/* 1 Not defined */ NOT_DEFINED
/* 2 Not defined */ NOT_DEFINED
/* 3 Not defined */ NOT_DEFINED
/* 4 Not defined */ NOT_DEFINED
/* 5 Not defined */ NOT_DEFINED
/* 6 Not defined */ NOT_DEFINED
/* 7 Not defined */ NOT_DEFINED
/* 8 Not defined */ NOT_DEFINED
/* 9 Not defined */ NOT_DEFINED
/* 10 Not defined */ NOT_DEFINED
/* 11 Not defined */ NOT_DEFINED
/* 12 Not defined */ NOT_DEFINED
/* 13 Not defined */ NOT_DEFINED
/* 14 Not defined */ NOT_DEFINED
/* 15 Not defined */ NOT_DEFINED
/* 16 Not defined */ NOT_DEFINED
/* 17 Not defined */ NOT_DEFINED
/* 18 Not defined */ NOT_DEFINED
/* 19 Not defined */ NOT_DEFINED
/* 20 Not defined */ NOT_DEFINED
/* 21 Not defined */ NOT_DEFINED
/* 22 Not defined */ NOT_DEFINED
/* 23 Not defined */ NOT_DEFINED
/* 24 Not defined */ NOT_DEFINED
/* 25 Not defined */ NOT_DEFINED
/* 26 Not defined */ NOT_DEFINED
/* 27 Not defined */ NOT_DEFINED
/* 28 Not defined */ NOT_DEFINED
/* 29 Not defined */ NOT_DEFINED
/* 30 Not defined */ NOT_DEFINED
/* 31 Not defined */ NOT_DEFINED
/* 32 Not defined */ NOT_DEFINED
/* 33 Not defined */ NOT_DEFINED
/* 34 Not defined */ NOT_DEFINED
/* 35 Acoustic Bass Drum */ 0x00, 0x00, 0x0d, 0x00, 0xe8, 0xa5, 0xef, 0xff, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 36 Bass Drum 1 */ 0x00, 0x00, 0x0b, 0x00, 0xa8, 0xd6, 0x4c, 0x4f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
/* 37 Side Stick */ NOT_DEFINED
/* 38 Acoustic Snare */ 0x2e, 0x02, 0x0a, 0x1b, 0xff, 0xf6, 0x0f, 0x4a, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, /* snare */
/* 39 Hand Clap */ NOT_DEFINED
/* 40 Electric Snare */ 0x0c, 0xd0, 0x00, 0x00, 0xc7, 0x70, 0xb4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* rksnare */
/* 41 Low Floor Tom */ NOT_DEFINED
/* 42 Closed Hi-Hat */ 0x64, 0x03, 0x02, 0x40, 0xb2, 0x97, 0xa2, 0xd4, 0x02, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, /* cymbal */
/* 43 High Floor Tom */ NOT_DEFINED
/* 44 Pedal Hi-Hat */ NOT_DEFINED
/* 45 Low Tom */ 0x01, 0x02, 0x00, 0x00, 0xfa, 0xda, 0xbf, 0xbf, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, /* tom */
/* 46 Open Hi-Hat */ NOT_DEFINED
/* 47 Low-Mid Tom */ 0x02, 0x30, 0x00, 0x00, 0xc8, 0xe0, 0x97, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* tom2 */
/* 48 Hi-Mid Tom */ NOT_DEFINED
/* 49 Crash Cymbal 1 */ NOT_DEFINED
/* 50 High Tom */ NOT_DEFINED
/* 51 Ride Cymbal 1 */ 0x64, 0x03, 0x00, 0x40, 0xb2, 0x97, 0x82, 0xd4, 0x02, 0x01, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x00, /* bcymbal */
/* 52 Chinese Cymbal */ NOT_DEFINED
/* 53 Ride Bell */ NOT_DEFINED
/* 54 Tambourine */ NOT_DEFINED
/* 55 Splash Cymbal */ NOT_DEFINED
/* 56 Cowbell */ NOT_DEFINED
/* 57 Crash Cymbal 2 */ NOT_DEFINED
/* 58 Vibrasl */ NOT_DEFINED
/* 59 Ride Cymbal */ NOT_DEFINED
/* 60 Hi Bon */ NOT_DEFINED
/* 61 Low Bon */ NOT_DEFINED
/* 62 Mute Hi Con */ NOT_DEFINED
/* 63 Open Hi Con */ NOT_DEFINED
/* 64 Low Con */ NOT_DEFINED
/* 65 High Timba */ NOT_DEFINED
/* 66 Low Timba */ NOT_DEFINED
/* 67 High Ago */ NOT_DEFINED
/* 68 Low Ago */ NOT_DEFINED
/* 69 Caba */ NOT_DEFINED
/* 70 Marac */ NOT_DEFINED
/* 71 Short Whist */ NOT_DEFINED
/* 72 Long Whist */ NOT_DEFINED
/* 73 Short Gui */ NOT_DEFINED
/* 74 Long Gui */ NOT_DEFINED
/* 75 Clav */ 0x13, 0x08, 0x80, 0x00, 0xfb, 0xe8, 0xff, 0xff, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, /* claves */
/* 76 Hi Wood Blo */ NOT_DEFINED
/* 77 Low Wood Blo */ NOT_DEFINED
/* 78 Mute Cui */ NOT_DEFINED
/* 79 Open Cui */ NOT_DEFINED
/* 80 Mute Triang */ NOT_DEFINED
/* 81 Open Triang */ 0x26, 0x1e, 0x03, 0x00, 0xe0, 0xff, 0xf0, 0x31, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, /* triangle */
/* 82 Not defined */ NOT_DEFINED
/* 83 Not defined */ NOT_DEFINED
/* 84 Not defined */ NOT_DEFINED
/* 85 Not defined */ NOT_DEFINED
/* 86 Not defined */ NOT_DEFINED
/* 87 Not defined */ NOT_DEFINED
/* 88 Not defined */ NOT_DEFINED
/* 89 Not defined */ NOT_DEFINED
/* 90 Not defined */ NOT_DEFINED
/* 91 Not defined */ NOT_DEFINED
/* 92 Not defined */ NOT_DEFINED
/* 93 Not defined */ NOT_DEFINED
/* 94 Not defined */ NOT_DEFINED
/* 95 Not defined */ NOT_DEFINED
/* 96 Not defined */ NOT_DEFINED
/* 97 Not defined */ NOT_DEFINED
/* 98 Not defined */ NOT_DEFINED
/* 99 Not defined */ NOT_DEFINED
/* 100 Not defined */ NOT_DEFINED
/* 101 Not defined */ NOT_DEFINED
/* 102 Not defined */ NOT_DEFINED
/* 103 Not defined */ NOT_DEFINED
/* 104 Not defined */ NOT_DEFINED
/* 105 Not defined */ NOT_DEFINED
/* 106 Not defined */ NOT_DEFINED
/* 107 Not defined */ NOT_DEFINED
/* 108 Not defined */ NOT_DEFINED
/* 109 Not defined */ NOT_DEFINED
/* 110 Not defined */ NOT_DEFINED
/* 111 Not defined */ NOT_DEFINED
/* 112 Not defined */ NOT_DEFINED
/* 113 Not defined */ NOT_DEFINED
/* 114 Not defined */ NOT_DEFINED
/* 115 Not defined */ NOT_DEFINED
/* 116 Not defined */ NOT_DEFINED
/* 117 Not defined */ NOT_DEFINED
/* 118 Not defined */ NOT_DEFINED
/* 119 Not defined */ NOT_DEFINED
/* 120 Not defined */ NOT_DEFINED
/* 121 Not defined */ NOT_DEFINED
/* 122 Not defined */ NOT_DEFINED
/* 123 Not defined */ NOT_DEFINED
/* 124 Not defined */ NOT_DEFINED
/* 125 Not defined */ NOT_DEFINED
/* 126 Not defined */ NOT_DEFINED
/* 127 Not defined */ NOT_DEFINED
/* 128 Not defined */ NOT_DEFINED
};

View file

@ -0,0 +1,57 @@
/*
* Sample AUXILIARY Wine Driver
*
* Copyright 1994 Martin Ayotte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <stdarg.h>
#include "windef.h"
#include "winbase.h"
#include "mmddk.h"
#include "audioclient.h"
#include "winternl.h"
#include "wine/debug.h"
#include "wine/unixlib.h"
#include "unixlib.h"
WINE_DEFAULT_DEBUG_CHANNEL(mmaux);
/**************************************************************************
* auxMessage (WINEOSS.2)
*/
DWORD WINAPI OSS_auxMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
DWORD_PTR dwParam1, DWORD_PTR dwParam2)
{
struct aux_message_params params;
UINT err;
TRACE("(%04X, %04X, %08IX, %08IX, %08IX);\n",
wDevID, wMsg, dwUser, dwParam1, dwParam2);
params.dev_id = wDevID;
params.msg = wMsg;
params.user = dwUser;
params.param_1 = dwParam1;
params.param_2 = dwParam2;
params.err = &err;
OSS_CALL(aux_message, &params);
return err;
}

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,32 @@
/*
* Copyright 2022 Huw Davies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include "../mmdevapi/unixlib.h"
NTSTATUS oss_midi_release(void *args);
NTSTATUS oss_midi_out_message(void *args);
NTSTATUS oss_midi_in_message(void *args);
NTSTATUS oss_midi_notify_wait(void *args);
#ifdef _WIN64
NTSTATUS oss_wow64_midi_out_message(void *args);
NTSTATUS oss_wow64_midi_in_message(void *args);
NTSTATUS oss_wow64_midi_notify_wait(void *args);
#endif
#define OSS_CALL(func, params) WINE_UNIX_CALL(func, params)

View file

@ -0,0 +1,11 @@
# WinMM driver functions
@ stdcall -private DriverProc(long long long long long) OSS_DriverProc
@ stdcall -private auxMessage(long long long long long) OSS_auxMessage
@ stdcall -private midMessage(long long long long long) OSS_midMessage
@ stdcall -private modMessage(long long long long long) OSS_modMessage
# MMDevAPI driver functions
@ stdcall -private GetPriority() AUDDRV_GetPriority
@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint
@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager

View file

@ -0,0 +1,9 @@
MODULE = winepulse.drv
IMPORTS = dxguid uuid winmm user32 advapi32 ole32
UNIX_LIBS = $(PULSE_LIBS) $(PTHREAD_LIBS)
UNIX_CFLAGS = $(PULSE_CFLAGS)
EXTRADLLFLAGS = -mcygwin
SOURCES = \
mmdevdrv.c

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
# MMDevAPI driver functions
@ stdcall -private GetPriority() AUDDRV_GetPriority
@ stdcall -private GetEndpointIDs(long ptr ptr ptr ptr) AUDDRV_GetEndpointIDs
@ stdcall -private GetAudioEndpoint(ptr ptr ptr) AUDDRV_GetAudioEndpoint
@ stdcall -private GetAudioSessionManager(ptr ptr) AUDDRV_GetAudioSessionManager
@ stdcall -private GetPropValue(ptr ptr ptr) AUDDRV_GetPropValue
# WinMM driver functions
@ stdcall -private DriverProc(long long long long long) winealsa.drv.DriverProc
@ stdcall -private midMessage(long long long long long) winealsa.drv.midMessage
@ stdcall -private modMessage(long long long long long) winealsa.drv.modMessage

64
pkgs/osu-wine/default.nix Normal file
View file

@ -0,0 +1,64 @@
## Configuration:
# Control you default wine config in nixpkgs-config:
# wine = {
# release = "stable"; # "stable", "unstable", "staging", "wayland"
# build = "wineWow"; # "wine32", "wine64", "wineWow"
# };
# Make additional configurations on demand:
# wine.override { wineBuild = "wine32"; wineRelease = "staging"; };
{ lib, stdenv, callPackage, darwin,
wineBuild ? if stdenv.hostPlatform.system == "x86_64-linux" then "wineWow" else "wine32",
gettextSupport ? true,
fontconfigSupport ? stdenv.isLinux,
alsaSupport ? stdenv.isLinux,
gtkSupport ? false,
openglSupport ? true,
tlsSupport ? true,
gstreamerSupport ? false,
cupsSupport ? true,
dbusSupport ? stdenv.isLinux,
openclSupport ? false,
cairoSupport ? stdenv.isLinux,
odbcSupport ? false,
netapiSupport ? false,
cursesSupport ? true,
vaSupport ? false,
pcapSupport ? false,
v4lSupport ? false,
saneSupport ? stdenv.isLinux,
gphoto2Support ? false,
krb5Support ? false,
pulseaudioSupport ? stdenv.isLinux,
udevSupport ? stdenv.isLinux,
xineramaSupport ? stdenv.isLinux,
vulkanSupport ? true,
sdlSupport ? true,
usbSupport ? true,
mingwSupport ? true,
waylandSupport ? stdenv.isLinux,
x11Support ? stdenv.isLinux,
embedInstallers ? false, # The Mono and Gecko MSI installers
moltenvk ? darwin.moltenvk # Allow users to override MoltenVK easily
}:
let wine-build = build: release:
lib.getAttr build (callPackage ./packages.nix {
wineRelease = release;
supportFlags = {
inherit
alsaSupport cairoSupport cupsSupport cursesSupport dbusSupport
embedInstallers fontconfigSupport gettextSupport gphoto2Support
gstreamerSupport gtkSupport krb5Support mingwSupport netapiSupport
odbcSupport openclSupport openglSupport pcapSupport
pulseaudioSupport saneSupport sdlSupport tlsSupport udevSupport
usbSupport v4lSupport vaSupport vulkanSupport waylandSupport
x11Support xineramaSupport
;
};
inherit moltenvk;
});
in
callPackage ./osu-wine.nix {
wineUnstable = wine-build wineBuild "unstable";
}

View file

@ -0,0 +1,35 @@
{ lib, callPackage, autoconf, hexdump, perl, python3, wineUnstable, path }:
with callPackage "${path}/pkgs/applications/emulators/wine/util.nix" {};
let patch = (callPackage ./sources.nix {}).staging;
build-inputs = pkgNames: extra:
(mkBuildInputs wineUnstable.pkgArches pkgNames) ++ extra;
patchList = lib.mapAttrsToList (k: v: ./patches/${k}) (builtins.readDir ./patches);
in assert lib.versions.majorMinor wineUnstable.version == lib.versions.majorMinor patch.version;
(lib.overrideDerivation (wineUnstable.override { wineRelease = "staging"; }) (self: {
buildInputs = build-inputs [ "perl" "util-linux" "autoconf" "gitMinimal" ] self.buildInputs;
nativeBuildInputs = [ autoconf hexdump perl python3 ] ++ self.nativeBuildInputs;
prePatch = self.prePatch or "" + ''
patchShebangs tools
cp -r ${patch}/patches ${patch}/staging .
chmod +w patches
patchShebangs ./patches/gitapply.sh
python3 ./staging/patchinstall.py DESTDIR="$PWD" --all ${lib.concatMapStringsSep " " (ps: "-W ${ps}") patch.disabledPatchsets}
for dir in $(ls ${./audio-revert}); do
rm -rf dlls/$dir
cp -r ${./audio-revert}/$dir dlls
chmod -R +w dlls/$dir
done
for patch in ${builtins.concatStringsSep " " patchList}; do
echo "Applying $patch"
patch -p1 < "$patch"
done
'';
})) // {
meta = wineUnstable.meta // {
description = wineUnstable.meta.description + " (with osu-wine patches)";
};
}

View file

@ -0,0 +1,57 @@
{ stdenv_32bit, lib, pkgs, pkgsi686Linux, pkgsCross, callPackage, substituteAll, moltenvk, path,
wineRelease ? "stable",
supportFlags
}:
let
src = lib.getAttr wineRelease (callPackage ./sources.nix {});
in with src; {
wine32 = pkgsi686Linux.callPackage "${path}/pkgs/applications/emulators/wine/base.nix" {
pname = "wine";
inherit src version supportFlags patches moltenvk wineRelease;
pkgArches = [ pkgsi686Linux ];
geckos = [ gecko32 ];
mingwGccs = with pkgsCross; [ mingw32.buildPackages.gcc ];
monos = [ mono ];
platforms = [ "i686-linux" "x86_64-linux" ];
};
wine64 = callPackage "${path}/pkgs/applications/emulators/wine/base.nix" {
pname = "wine64";
inherit src version supportFlags patches moltenvk wineRelease;
pkgArches = [ pkgs ];
mingwGccs = with pkgsCross; [ mingwW64.buildPackages.gcc ];
geckos = [ gecko64 ];
monos = [ mono ];
configureFlags = [ "--enable-win64" ];
platforms = [ "x86_64-linux" "x86_64-darwin" ];
mainProgram = "wine64";
};
wineWow = callPackage "${path}/pkgs/applications/emulators/wine/base.nix" {
pname = "wine-wow";
inherit src version supportFlags patches moltenvk wineRelease;
stdenv = stdenv_32bit;
pkgArches = [ pkgs pkgsi686Linux ];
geckos = [ gecko32 gecko64 ];
mingwGccs = with pkgsCross; [ mingw32.buildPackages.gcc mingwW64.buildPackages.gcc ];
monos = [ mono ];
buildScript = substituteAll {
src = "${path}/pkgs/applications/emulators/wine/builder-wow.sh";
# pkgconfig has trouble picking the right architecture
pkgconfig64remove = lib.makeSearchPathOutput "dev" "lib/pkgconfig" [ pkgs.glib pkgs.gst_all_1.gstreamer ];
};
platforms = [ "x86_64-linux" ];
mainProgram = "wine64";
};
wineWow64 = callPackage "${path}/pkgs/applications/emulators/wine/base.nix" {
pname = "wine-wow64";
inherit src version patches moltenvk wineRelease;
supportFlags = supportFlags // { mingwSupport = true; }; # Required because we request "--enable-archs=x86_64"
pkgArches = [ pkgs ];
mingwGccs = with pkgsCross; [ mingw32.buildPackages.gcc mingwW64.buildPackages.gcc ];
geckos = [ gecko64 ];
monos = [ mono ];
configureFlags = [ "--enable-archs=x86_64,i386" ];
platforms = [ "x86_64-linux" "x86_64-darwin" ];
mainProgram = "wine";
};
}

View file

@ -0,0 +1,207 @@
--- a/include/wine/test.h
+++ b/include/wine/test.h
@@ -28,6 +28,13 @@
#include <winbase.h>
#include <wine/debug.h>
+#ifdef __WINE_CONFIG_H
+#error config.h should not be used in Wine tests
+#endif
+#ifdef __WINE_WINE_UNICODE_H
+#error wine/unicode.h should not be used in Wine tests
+#endif
+
#ifndef INVALID_FILE_ATTRIBUTES
#define INVALID_FILE_ATTRIBUTES (~0u)
#endif
--- /dev/null
+++ b/include/wine/unicode.h
@@ -0,0 +1,178 @@
+/*
+ * Wine internal Unicode definitions
+ *
+ * Copyright 2000 Alexandre Julliard
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#if 0
+#pragma makedep install
+#endif
+
+#ifndef __WINE_WINE_UNICODE_H
+#define __WINE_WINE_UNICODE_H
+
+#include <stdarg.h>
+
+#include <windef.h>
+#include <winbase.h>
+#include <winnls.h>
+#include <winternl.h>
+
+#ifdef __WINE_USE_MSVCRT
+#error This file should not be used with msvcrt headers
+#endif
+
+#ifndef WINE_UNICODE_INLINE
+#define WINE_UNICODE_INLINE static FORCEINLINE
+#endif
+
+WINE_UNICODE_INLINE WCHAR tolowerW( WCHAR ch )
+{
+ return RtlDowncaseUnicodeChar( ch );
+}
+
+WINE_UNICODE_INLINE WCHAR toupperW( WCHAR ch )
+{
+ return RtlUpcaseUnicodeChar( ch );
+}
+
+WINE_UNICODE_INLINE int isspaceW( WCHAR wc )
+{
+ unsigned short type;
+ GetStringTypeW( CT_CTYPE1, &wc, 1, &type );
+ return type & C1_SPACE;
+}
+
+WINE_UNICODE_INLINE unsigned int strlenW( const WCHAR *str )
+{
+ const WCHAR *s = str;
+ while (*s) s++;
+ return s - str;
+}
+
+WINE_UNICODE_INLINE WCHAR *strcpyW( WCHAR *dst, const WCHAR *src )
+{
+ WCHAR *p = dst;
+ while ((*p++ = *src++));
+ return dst;
+}
+
+WINE_UNICODE_INLINE WCHAR *strcatW( WCHAR *dst, const WCHAR *src )
+{
+ strcpyW( dst + strlenW(dst), src );
+ return dst;
+}
+
+WINE_UNICODE_INLINE WCHAR *strrchrW( const WCHAR *str, WCHAR ch )
+{
+ WCHAR *ret = NULL;
+ do { if (*str == ch) ret = (WCHAR *)(ULONG_PTR)str; } while (*str++);
+ return ret;
+}
+
+WINE_UNICODE_INLINE int strcmpiW( const WCHAR *str1, const WCHAR *str2 )
+{
+ for (;;)
+ {
+ int ret = tolowerW(*str1) - tolowerW(*str2);
+ if (ret || !*str1) return ret;
+ str1++;
+ str2++;
+ }
+}
+
+WINE_UNICODE_INLINE int strncmpiW( const WCHAR *str1, const WCHAR *str2, int n )
+{
+ int ret = 0;
+ for ( ; n > 0; n--, str1++, str2++)
+ if ((ret = tolowerW(*str1) - tolowerW(*str2)) || !*str1) break;
+ return ret;
+}
+
+WINE_UNICODE_INLINE LONG strtolW( LPCWSTR s, LPWSTR *end, INT base )
+{
+ BOOL negative = FALSE, empty = TRUE;
+ LONG ret = 0;
+
+ if (base < 0 || base == 1 || base > 36) return 0;
+ if (end) *end = (WCHAR *)s;
+ while (isspaceW(*s)) s++;
+
+ if (*s == '-')
+ {
+ negative = TRUE;
+ s++;
+ }
+ else if (*s == '+') s++;
+
+ if ((base == 0 || base == 16) && s[0] == '0' && (s[1] == 'x' || s[1] == 'X'))
+ {
+ base = 16;
+ s += 2;
+ }
+ if (base == 0) base = s[0] != '0' ? 10 : 8;
+
+ while (*s)
+ {
+ int v;
+
+ if ('0' <= *s && *s <= '9') v = *s - '0';
+ else if ('A' <= *s && *s <= 'Z') v = *s - 'A' + 10;
+ else if ('a' <= *s && *s <= 'z') v = *s - 'a' + 10;
+ else break;
+ if (v >= base) break;
+ if (negative) v = -v;
+ s++;
+ empty = FALSE;
+
+ if (!negative && (ret > MAXLONG / base || ret * base > MAXLONG - v))
+ ret = MAXLONG;
+ else if (negative && (ret < (LONG)MINLONG / base || ret * base < (LONG)(MINLONG - v)))
+ ret = MINLONG;
+ else
+ ret = ret * base + v;
+ }
+
+ if (end && !empty) *end = (WCHAR *)s;
+ return ret;
+}
+
+NTSYSAPI int __cdecl _vsnwprintf(WCHAR*,size_t,const WCHAR*,__ms_va_list);
+
+static inline int WINAPIV snprintfW( WCHAR *str, size_t len, const WCHAR *format, ...)
+{
+ int retval;
+ __ms_va_list valist;
+ __ms_va_start(valist, format);
+ retval = _vsnwprintf(str, len, format, valist);
+ __ms_va_end(valist);
+ return retval;
+}
+
+static inline int WINAPIV sprintfW( WCHAR *str, const WCHAR *format, ...)
+{
+ int retval;
+ __ms_va_list valist;
+ __ms_va_start(valist, format);
+ retval = _vsnwprintf(str, MAXLONG, format, valist);
+ __ms_va_end(valist);
+ return retval;
+}
+
+#undef WINE_UNICODE_INLINE
+
+#endif /* __WINE_WINE_UNICODE_H */
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -873,6 +873,7 @@
wine/strmbase.h \
wine/svcctl.idl \
wine/test.h \
+ wine/unicode.h \
wine/unixlib.h \
wine/vulkan.h \
wine/vulkan_driver.h \

View file

@ -0,0 +1,24 @@
From 1b5de04e1ae401f2f3d7179da0379191886cdfad Mon Sep 17 00:00:00 2001
From: Torge Matthies <tmatthies@codeweavers.com>
Date: Tue, 2 May 2023 01:36:12 +0200
Subject: [PATCH] libs/libjpeg: Set default DCT algorithm to fastest.
---
libs/jpeg/jconfig.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/libs/jpeg/jconfig.h b/libs/jpeg/jconfig.h
index 2d05a3b09026..9f18c71751bd 100644
--- a/libs/jpeg/jconfig.h
+++ b/libs/jpeg/jconfig.h
@@ -17,6 +17,7 @@
/* #undef NEED_SHORT_EXTERNAL_NAMES */
/* Define this if you get warnings about undefined structures. */
/* #undef INCOMPLETE_TYPES_BROKEN */
+#define JDCT_DEFAULT JDCT_FASTEST
/* Define "boolean" as unsigned char, not enum, on Windows systems. */
#ifdef _WIN32
--
2.40.1

View file

@ -0,0 +1,253 @@
--- b/dlls/winex11.drv/event.c
+++ a/dlls/winex11.drv/event.c
@@ -604,27 +604,16 @@
*/
static void set_focus( Display *display, HWND hwnd, Time time )
{
+ HWND focus;
- HWND focus, old_active;
Window win;
GUITHREADINFO threadinfo;
- old_active = NtUserGetForegroundWindow();
-
/* prevent recursion */
x11drv_thread_data()->active_window = hwnd;
TRACE( "setting foreground window to %p\n", hwnd );
NtUserSetForegroundWindow( hwnd );
- /* Some applications expect that a being deactivated topmost window
- * receives the WM_WINDOWPOSCHANGING/WM_WINDOWPOSCHANGED messages,
- * and perform some specific actions. Chessmaster is one of such apps.
- * Window Manager keeps a topmost window on top in z-oder, so there is
- * no need to actually do anything, just send the messages.
- */
- if (old_active && (NtUserGetWindowLongW( old_active, GWL_EXSTYLE ) & WS_EX_TOPMOST))
- NtUserSetWindowPos( old_active, hwnd, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE | SWP_NOOWNERZORDER );
-
threadinfo.cbSize = sizeof(threadinfo);
NtUserGetGUIThreadInfo( 0, &threadinfo );
focus = threadinfo.hwndFocus;
--- b/dlls/win32u/input.c
+++ a/dlls/win32u/input.c
@@ -1375,9 +1375,6 @@
send_message( hwnd, WM_ACTIVATE,
MAKEWPARAM( mouse ? WA_CLICKACTIVE : WA_ACTIVE, is_iconic(hwnd) ),
(LPARAM)previous );
-
- send_message( hwnd, WM_NCPOINTERUP, 0, 0);
-
if (NtUserGetAncestor( hwnd, GA_PARENT ) == get_desktop_window())
NtUserPostMessage( get_desktop_window(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd );
--- b/dlls/win32u/input.c
+++ a/dlls/win32u/input.c
@@ -1633,10 +1633,6 @@
(LPARAM)previous );
if (NtUserGetAncestor( hwnd, GA_PARENT ) == get_desktop_window())
NtUserPostMessage( get_desktop_window(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd );
-
- if (hwnd == NtUserGetForegroundWindow() && !is_iconic( hwnd ))
- NtUserSetActiveWindow( hwnd );
-
}
user_driver->pSetActiveWindow( hwnd );
--- b/dlls/win32u/driver.c
+++ a/dlls/win32u/driver.c
@@ -838,10 +838,6 @@
hdc, rect.left - dx, rect.top - dy, SRCCOPY, 0, 0 );
}
-static void nulldrv_SetActiveWindow( HWND hwnd )
-{
-}
-
static void nulldrv_SetCapture( HWND hwnd, UINT flags )
{
}
@@ -1245,7 +1241,6 @@
nulldrv_ProcessEvents,
nulldrv_ReleaseDC,
nulldrv_ScrollDC,
- nulldrv_SetActiveWindow,
nulldrv_SetCapture,
loaderdrv_SetDesktopWindow,
nulldrv_SetFocus,
@@ -1325,7 +1320,6 @@
SET_USER_FUNC(ProcessEvents);
SET_USER_FUNC(ReleaseDC);
SET_USER_FUNC(ScrollDC);
- SET_USER_FUNC(SetActiveWindow);
SET_USER_FUNC(SetCapture);
SET_USER_FUNC(SetDesktopWindow);
SET_USER_FUNC(SetFocus);
--- b/dlls/win32u/input.c
+++ a/dlls/win32u/input.c
@@ -1887,8 +1887,6 @@
NtUserPostMessage( get_desktop_window(), WM_PARENTNOTIFY, WM_NCACTIVATE, (LPARAM)hwnd );
}
- user_driver->pSetActiveWindow( hwnd );
-
/* now change focus if necessary */
if (focus)
{
--- b/dlls/winex11.drv/event.c
+++ a/dlls/winex11.drv/event.c
@@ -576,9 +576,6 @@
Window win;
GUITHREADINFO threadinfo;
- /* prevent recursion */
- x11drv_thread_data()->active_window = hwnd;
-
TRACE( "setting foreground window to %p\n", hwnd );
NtUserSetForegroundWindow( hwnd );
@@ -836,8 +833,6 @@
if (!focus_win)
{
- x11drv_thread_data()->active_window = 0;
-
/* Abey : 6-Oct-99. Check again if the focus out window is the
Foreground window, because in most cases the messages sent
above must have already changed the foreground window, in which
--- b/dlls/winex11.drv/init.c
+++ a/dlls/winex11.drv/init.c
@@ -421,7 +421,6 @@
.pProcessEvents = X11DRV_ProcessEvents,
.pReleaseDC = X11DRV_ReleaseDC,
.pScrollDC = X11DRV_ScrollDC,
- .pSetActiveWindow = X11DRV_SetActiveWindow,
.pSetCapture = X11DRV_SetCapture,
.pSetDesktopWindow = X11DRV_SetDesktopWindow,
.pSetFocus = X11DRV_SetFocus,
--- b/dlls/winex11.drv/window.c
+++ a/dlls/winex11.drv/window.c
@@ -2431,54 +2431,6 @@
}
-/***********************************************************************
- * SetActiveWindow (X11DRV.@)
- */
-void X11DRV_SetActiveWindow( HWND hwnd )
-{
- struct x11drv_thread_data *thread_data = x11drv_init_thread_data();
- struct x11drv_win_data *data;
-
- TRACE("%p\n", hwnd);
-
- if (thread_data->active_window == hwnd)
- {
- TRACE("ignoring activation for already active window %p\n", hwnd);
- return;
- }
-
- if (!(data = get_win_data( hwnd ))) return;
-
- if (data->mapped && data->managed && !data->iconic)
- {
- XEvent xev;
- struct x11drv_win_data *active = get_win_data( thread_data->active_window );
- DWORD timestamp = NtUserGetThreadInfo()->message_time - EVENT_x11_time_to_win32_time( 0 );
-
- TRACE("setting _NET_ACTIVE_WINDOW to %p/%lx, current active %p/%lx\n",
- data->hwnd, data->whole_window, active ? active->hwnd : NULL, active ? active->whole_window : 0 );
-
- xev.xclient.type = ClientMessage;
- xev.xclient.window = data->whole_window;
- xev.xclient.message_type = x11drv_atom(_NET_ACTIVE_WINDOW);
- xev.xclient.serial = 0;
- xev.xclient.display = data->display;
- xev.xclient.send_event = True;
- xev.xclient.format = 32;
-
- xev.xclient.data.l[0] = 1; /* source: application */
- xev.xclient.data.l[1] = timestamp;
- xev.xclient.data.l[2] = active ? active->whole_window : 0;
- xev.xclient.data.l[3] = 0;
- xev.xclient.data.l[4] = 0;
- XSendEvent( data->display, root_window, False, SubstructureRedirectMask | SubstructureNotifyMask, &xev );
-
- if (active) release_win_data( active );
- }
-
- release_win_data( data );
-}
-
/***********************************************************************
* SetCapture (X11DRV.@)
*/
--- b/dlls/winex11.drv/x11drv.h
+++ a/dlls/winex11.drv/x11drv.h
@@ -231,7 +231,6 @@
const RECT *top_rect, DWORD flags );
extern void X11DRV_ReleaseDC( HWND hwnd, HDC hdc );
extern BOOL X11DRV_ScrollDC( HDC hdc, INT dx, INT dy, HRGN update );
-extern void X11DRV_SetActiveWindow( HWND hwnd );
extern void X11DRV_SetCapture( HWND hwnd, UINT flags );
extern void X11DRV_SetDesktopWindow( HWND hwnd );
extern void X11DRV_SetLayeredWindowAttributes( HWND hwnd, COLORREF key, BYTE alpha,
@@ -383,7 +382,6 @@
Display *display;
XEvent *current_event; /* event currently being processed */
HWND grab_hwnd; /* window that currently grabs the mouse */
- HWND active_window; /* active window */
HWND last_focus; /* last window that had focus */
XIM xim; /* input method */
HWND last_xic_hwnd; /* last xic window */
@@ -490,7 +488,6 @@
XATOM__ICC_PROFILE,
XATOM__KDE_NET_WM_STATE_SKIP_SWITCHER,
XATOM__MOTIF_WM_HINTS,
- XATOM__NET_ACTIVE_WINDOW,
XATOM__NET_STARTUP_INFO_BEGIN,
XATOM__NET_STARTUP_INFO,
XATOM__NET_SUPPORTED,
--- b/dlls/winex11.drv/x11drv_main.c
+++ a/dlls/winex11.drv/x11drv_main.c
@@ -154,7 +154,6 @@
"_ICC_PROFILE",
"_KDE_NET_WM_STATE_SKIP_SWITCHER",
"_MOTIF_WM_HINTS",
- "_NET_ACTIVE_WINDOW",
"_NET_STARTUP_INFO_BEGIN",
"_NET_STARTUP_INFO",
"_NET_SUPPORTED",
--- b/include/wine/gdi_driver.h
+++ a/include/wine/gdi_driver.h
@@ -316,7 +316,6 @@
BOOL (*pProcessEvents)(DWORD);
void (*pReleaseDC)(HWND,HDC);
BOOL (*pScrollDC)(HDC,INT,INT,HRGN);
- void (*pSetActiveWindow)(HWND);
void (*pSetCapture)(HWND,UINT);
void (*pSetDesktopWindow)(HWND);
void (*pSetFocus)(HWND);
--- b/dlls/winex11.drv/window.c
+++ a/dlls/winex11.drv/window.c
@@ -278,6 +278,9 @@
if (style & WS_MINIMIZEBOX) ret |= MWM_DECOR_MINIMIZE;
if (style & WS_MAXIMIZEBOX) ret |= MWM_DECOR_MAXIMIZE;
}
+ if (ex_style & WS_EX_DLGMODALFRAME) ret |= MWM_DECOR_BORDER;
+ else if (style & WS_THICKFRAME) ret |= MWM_DECOR_BORDER;
+ else if ((style & (WS_DLGFRAME|WS_BORDER)) == WS_DLGFRAME) ret |= MWM_DECOR_BORDER;
return ret;
}
--- b/dlls/winex11.drv/window.c
+++ a/dlls/winex11.drv/window.c
@@ -279,7 +279,7 @@
if (style & WS_MAXIMIZEBOX) ret |= MWM_DECOR_MAXIMIZE;
}
if (ex_style & WS_EX_DLGMODALFRAME) ret |= MWM_DECOR_BORDER;
+ else if (style & WS_THICKFRAME) ret |= MWM_DECOR_BORDER | MWM_DECOR_RESIZEH;
- else if (style & WS_THICKFRAME) ret |= MWM_DECOR_BORDER;
else if ((style & (WS_DLGFRAME|WS_BORDER)) == WS_DLGFRAME) ret |= MWM_DECOR_BORDER;
return ret;
}

View file

@ -0,0 +1,292 @@
commit 6a033150c36bea6d704b7537c219e9b13b4387ec
Author: Rémi Bernon <rbernon@codeweavers.com>
Date: Tue Dec 1 22:49:26 2020 +0100
Subject: [PATCH 1/3] server: Implement thread priorities on Linux.
This does not report permission errors in order to avoid any breaking
change, only the parameter checks that were already there are returning
errors.
Only call setpriority on Linux, as unix_tid is a Mach port on Mac OS.
--- a/configure.ac
+++ b/configure.ac
@@ -2086,6 +2086,25 @@
AC_DEFINE(HAVE_SCHED_SETAFFINITY, 1, [Define to 1 if you have the `sched_setaffinity' function.])
fi
+AC_CACHE_CHECK([for sched_setscheduler],wine_cv_have_sched_setscheduler,
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[#define _GNU_SOURCE
+#include <sched.h>]], [[sched_setscheduler(0, 0, 0);]])],[wine_cv_have_sched_setscheduler=yes],[wine_cv_have_sched_setscheduler=no]))
+if test "$wine_cv_have_sched_setscheduler" = "yes"
+then
+ AC_DEFINE(HAVE_SCHED_SETSCHEDULER, 1, [Define to 1 if you have the `sched_setscheduler' function.])
+fi
+
+AC_CACHE_CHECK([for setpriority],wine_cv_have_setpriority,
+ AC_LINK_IFELSE([AC_LANG_PROGRAM(
+[[#define _GNU_SOURCE
+#include <sys/resource.h>
+#include <sys/time.h>]], [[setpriority(0, 0, 0);]])],[wine_cv_have_setpriority=yes],[wine_cv_have_setpriority=no]))
+if test "$wine_cv_have_setpriority" = "yes"
+then
+ AC_DEFINE(HAVE_SETPRIORITY, 1, [Define to 1 if you have the `setpriority' function.])
+fi
+
dnl **** Check for types ****
AC_C_INLINE
--- a/server/process.c
+++ b/server/process.c
@@ -1638,6 +1638,24 @@
release_object( process );
}
+static void set_process_priority( struct process *process, int priority )
+{
+ struct thread *thread;
+
+ if (!process->running_threads)
+ {
+ set_error( STATUS_PROCESS_IS_TERMINATING );
+ return;
+ }
+
+ LIST_FOR_EACH_ENTRY( thread, &process->thread_list, struct thread, proc_entry )
+ {
+ set_thread_priority( thread, priority, thread->priority );
+ }
+
+ process->priority = priority;
+}
+
static void set_process_affinity( struct process *process, affinity_t affinity )
{
struct thread *thread;
@@ -1663,7 +1681,7 @@
if ((process = get_process_from_handle( req->handle, PROCESS_SET_INFORMATION )))
{
- if (req->mask & SET_PROCESS_INFO_PRIORITY) process->priority = req->priority;
+ if (req->mask & SET_PROCESS_INFO_PRIORITY) set_process_priority( process, req->priority );
if (req->mask & SET_PROCESS_INFO_AFFINITY) set_process_affinity( process, req->affinity );
release_object( process );
}
--- a/server/thread.c
+++ b/server/thread.c
@@ -37,6 +37,12 @@
#define _WITH_CPU_SET_T
#include <sched.h>
#endif
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
#include "ntstatus.h"
#define WIN32_NO_STATUS
@@ -253,6 +259,7 @@
thread->state = RUNNING;
thread->exit_code = 0;
thread->priority = 0;
+ thread->priority_applied = 0;
thread->suspend = 0;
thread->dbg_hidden = 0;
thread->desktop_users = 0;
@@ -648,31 +655,151 @@
return mask;
}
+#if defined(HAVE_SCHED_SETSCHEDULER) || defined(HAVE_SETPRIORITY)
+static int get_unix_priority( int priority_class, int priority )
+{
+ switch (priority_class) {
+ case PROCESS_PRIOCLASS_IDLE:
+ switch (priority) {
+ case THREAD_PRIORITY_IDLE: return 15;
+ case THREAD_PRIORITY_LOWEST: return 10;
+ case THREAD_PRIORITY_BELOW_NORMAL: return 8;
+ case THREAD_PRIORITY_NORMAL: return 6;
+ case THREAD_PRIORITY_ABOVE_NORMAL: return 4;
+ case THREAD_PRIORITY_HIGHEST: return 2;
+ case THREAD_PRIORITY_TIME_CRITICAL: return -15;
+ }
+ case PROCESS_PRIOCLASS_BELOW_NORMAL:
+ switch (priority) {
+ case THREAD_PRIORITY_IDLE: return 15;
+ case THREAD_PRIORITY_LOWEST: return 8;
+ case THREAD_PRIORITY_BELOW_NORMAL: return 6;
+ case THREAD_PRIORITY_NORMAL: return 4;
+ case THREAD_PRIORITY_ABOVE_NORMAL: return 2;
+ case THREAD_PRIORITY_HIGHEST: return 0;
+ case THREAD_PRIORITY_TIME_CRITICAL: return -15;
+ }
+ case PROCESS_PRIOCLASS_NORMAL:
+ switch (priority) {
+ case THREAD_PRIORITY_IDLE: return 15;
+ case THREAD_PRIORITY_LOWEST: return 4;
+ case THREAD_PRIORITY_BELOW_NORMAL: return 2;
+ case THREAD_PRIORITY_NORMAL: return 0;
+ case THREAD_PRIORITY_ABOVE_NORMAL: return -2;
+ case THREAD_PRIORITY_HIGHEST: return -4;
+ case THREAD_PRIORITY_TIME_CRITICAL: return -15;
+ }
+ case PROCESS_PRIOCLASS_ABOVE_NORMAL:
+ switch (priority) {
+ case THREAD_PRIORITY_IDLE: return 15;
+ case THREAD_PRIORITY_LOWEST: return 0;
+ case THREAD_PRIORITY_BELOW_NORMAL: return -2;
+ case THREAD_PRIORITY_NORMAL: return -4;
+ case THREAD_PRIORITY_ABOVE_NORMAL: return -6;
+ case THREAD_PRIORITY_HIGHEST: return -8;
+ case THREAD_PRIORITY_TIME_CRITICAL: return -15;
+ }
+ case PROCESS_PRIOCLASS_HIGH:
+ switch (priority) {
+ case THREAD_PRIORITY_IDLE: return 15;
+ case THREAD_PRIORITY_LOWEST: return -2;
+ case THREAD_PRIORITY_BELOW_NORMAL: return -4;
+ case THREAD_PRIORITY_NORMAL: return -6;
+ case THREAD_PRIORITY_ABOVE_NORMAL: return -8;
+ case THREAD_PRIORITY_HIGHEST: return -10;
+ case THREAD_PRIORITY_TIME_CRITICAL: return -15;
+ }
+ case PROCESS_PRIOCLASS_REALTIME:
+ switch (priority) {
+ case THREAD_PRIORITY_IDLE: return 1;
+ case -7:
+ case -6:
+ case -5:
+ case -4:
+ case -3:
+ case THREAD_PRIORITY_LOWEST:
+ case THREAD_PRIORITY_BELOW_NORMAL:
+ case THREAD_PRIORITY_NORMAL:
+ case THREAD_PRIORITY_ABOVE_NORMAL:
+ case THREAD_PRIORITY_HIGHEST:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ return priority + 9;
+ case THREAD_PRIORITY_TIME_CRITICAL:
+ return 16;
+ }
+ }
+ return 0;
+}
+#endif
+
#define THREAD_PRIORITY_REALTIME_HIGHEST 6
#define THREAD_PRIORITY_REALTIME_LOWEST -7
+int set_thread_priority( struct thread* thread, int priority_class, int priority )
+{
+ int max = THREAD_PRIORITY_HIGHEST;
+ int min = THREAD_PRIORITY_LOWEST;
+ if (priority_class == PROCESS_PRIOCLASS_REALTIME)
+ {
+ max = THREAD_PRIORITY_REALTIME_HIGHEST;
+ min = THREAD_PRIORITY_REALTIME_LOWEST;
+ }
+
+ if ((priority < min || priority > max) &&
+ priority != THREAD_PRIORITY_IDLE &&
+ priority != THREAD_PRIORITY_TIME_CRITICAL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ if (thread->process->priority == priority_class &&
+ thread->priority == priority &&
+ thread->priority_applied)
+ return 0;
+
+ thread->priority = priority;
+ thread->priority_applied = 0;
+ if (thread->unix_tid == -1)
+ return 0;
+
+#ifdef __linux__
+ if (priority_class == PROCESS_PRIOCLASS_REALTIME)
+ {
+#ifdef HAVE_SCHED_SETSCHEDULER
+ struct sched_param param;
+ if (sched_getparam( thread->unix_tid, &param ) != 0)
+ return 0; /* ignore errors for now */
+
+ param.sched_priority = get_unix_priority( priority_class, priority );
+ if (sched_setscheduler( thread->unix_tid, SCHED_RR|SCHED_RESET_ON_FORK, &param ) == 0)
+ return 0;
+#endif
+ }
+ else
+ {
+#ifdef HAVE_SETPRIORITY
+ if (setpriority( PRIO_PROCESS, thread->unix_tid,
+ get_unix_priority( priority_class, priority ) ) == 0)
+ return 0;
+#endif
+ }
+#endif
+
+ return 0; /* ignore errors for now */
+}
+
/* set all information about a thread */
static void set_thread_info( struct thread *thread,
const struct set_thread_info_request *req )
{
if (req->mask & SET_THREAD_INFO_PRIORITY)
{
- int max = THREAD_PRIORITY_HIGHEST;
- int min = THREAD_PRIORITY_LOWEST;
- if (thread->process->priority == PROCESS_PRIOCLASS_REALTIME)
- {
- max = THREAD_PRIORITY_REALTIME_HIGHEST;
- min = THREAD_PRIORITY_REALTIME_LOWEST;
- }
- if ((req->priority >= min && req->priority <= max) ||
- req->priority == THREAD_PRIORITY_IDLE ||
- req->priority == THREAD_PRIORITY_TIME_CRITICAL)
- {
- thread->priority = req->priority;
- set_scheduler_priority( thread );
- }
- else
- set_error( STATUS_INVALID_PARAMETER );
+ if (set_thread_priority( thread, thread->process->priority, req->priority ))
+ file_set_error();
}
if (req->mask & SET_THREAD_INFO_AFFINITY)
{
@@ -1541,6 +1668,7 @@
init_thread_context( current );
generate_debug_event( current, DbgCreateThreadStateChange, &req->entry );
+ set_thread_priority( current, current->process->priority, current->priority );
set_thread_affinity( current, current->affinity );
reply->suspend = (current->suspend || current->process->suspend || current->context != NULL);
--- a/server/thread.h
+++ b/server/thread.h
@@ -84,6 +84,7 @@
client_ptr_t entry_point; /* entry point (in client address space) */
affinity_t affinity; /* affinity mask */
int priority; /* priority level */
+ int priority_applied; /* priority level successfully applied status */
int suspend; /* suspend count */
int dbg_hidden; /* hidden from debugger */
obj_handle_t desktop; /* desktop handle */
@@ -124,6 +125,7 @@
extern int thread_add_inflight_fd( struct thread *thread, int client, int server );
extern int thread_get_inflight_fd( struct thread *thread, int client );
extern struct token *thread_get_impersonation_token( struct thread *thread );
+extern int set_thread_priority( struct thread *thread, int priority_class, int priority );
extern int set_thread_affinity( struct thread *thread, affinity_t affinity );
extern int suspend_thread( struct thread *thread );
extern int resume_thread( struct thread *thread );

View file

@ -0,0 +1,17 @@
--- b/include/mmddk.h
+++ a/include/mmddk.h
@@ -30,6 +30,14 @@
extern "C" {
#endif
+#define MAX_MIDIINDRV (16)
+/* For now I'm making 16 the maximum number of midi devices one can
+ * have. This should be more than enough for everybody. But as a purist,
+ * I intend to make it unbounded in the future, as soon as I figure
+ * a good way to do so.
+ */
+#define MAX_MIDIOUTDRV (16)
+
/* ==================================
* Multimedia DDK compatible part
* ================================== */

View file

@ -0,0 +1,198 @@
--- a/tools/makedep.c
+++ b/tools/makedep.c
@@ -199,6 +199,7 @@
const char *staticlib;
const char *importlib;
const char *unixlib;
+ int use_msvcrt;
int data_only;
int is_win16;
int is_exe;
@@ -602,17 +603,6 @@
/*******************************************************************
- * is_using_msvcrt
- *
- * Check if the files of a makefile use msvcrt by default.
- */
-static int is_using_msvcrt( struct makefile *make )
-{
- return make->module || make->testdll;
-}
-
-
-/*******************************************************************
* arch_module_name
*/
static char *arch_module_name( const char *module, unsigned int arch )
@@ -870,7 +860,7 @@
file->basename = xstrdup( filename ? filename : name );
file->filename = obj_dir_path( make, file->basename );
file->file->flags = FLAG_GENERATED;
- file->use_msvcrt = is_using_msvcrt( make );
+ file->use_msvcrt = make->use_msvcrt;
list_add_tail( &make->sources, &file->entry );
if (make == include_makefile)
{
@@ -1620,7 +1610,7 @@
memset( file, 0, sizeof(*file) );
file->name = xstrdup(name);
- file->use_msvcrt = is_using_msvcrt( make );
+ file->use_msvcrt = make->use_msvcrt;
file->is_external = !!make->extlib;
list_add_tail( &make->sources, &file->entry );
if (make == include_makefile)
@@ -1818,12 +1808,13 @@
unsigned int i, arch;
struct incl_file *source, *next, *file, *dlldata = NULL;
struct strarray objs = get_expanded_make_var_array( make, "EXTRA_OBJS" );
+ int multiarch = archs.count > 1 && make->use_msvcrt;
LIST_FOR_EACH_ENTRY_SAFE( source, next, &make->sources, struct incl_file, entry )
{
for (arch = 0; arch < archs.count; arch++)
{
- if (!is_multiarch( arch )) continue;
+ if (!arch != !multiarch) continue;
if (source->file->flags & FLAG_IDL_CLIENT)
{
file = add_generated_source( make, replace_extension( source->name, ".idl", "_c.c" ), NULL, arch );
@@ -1942,7 +1933,7 @@
{
for (arch = 0; arch < archs.count; arch++)
{
- if (!is_multiarch( arch )) continue;
+ if (!arch != !multiarch) continue;
file = add_generated_source( make, "testlist.o", "testlist.c", arch );
add_dependency( file->file, "wine/test.h", INCL_NORMAL );
add_all_includes( make, file, file->file );
@@ -2196,6 +2187,7 @@
*/
static const char *get_default_crt( const struct makefile *make )
{
+ if (!make->use_msvcrt) return NULL;
if (make->module && is_crt_module( make->module )) return NULL; /* don't add crt import to crt dlls */
return !make->testdll && (!make->staticlib || make->extlib) ? "ucrtbase" : "msvcrt";
}
@@ -2352,7 +2344,6 @@
strarray_add( &ret, strmake( "-I%s", root_src_dir_path( "include/msvcrt" )));
for (i = 0; i < make->include_paths.count; i++)
strarray_add( &ret, strmake( "-I%s", make->include_paths.str[i] ));
- strarray_add( &ret, get_crt_define( make ));
}
strarray_addall( &ret, make->define_args );
strarray_addall( &ret, get_expanded_file_local_var( make, obj, "EXTRADEFS" ));
@@ -2412,9 +2403,7 @@
output_filename( tools_path( make, "winebuild" ));
}
output_filenames( target_flags[arch] );
- if (arch) return;
- output_filename( "-mno-cygwin" );
- output_filenames( lddll_flags );
+ if (!arch) output_filenames( lddll_flags );
}
@@ -2816,6 +2805,7 @@
struct strarray multiarch_targets[MAX_ARCHS] = { empty_strarray };
const char *dest;
unsigned int i, arch;
+ int multiarch;
if (find_include_file( make, strmake( "%s.h", obj ))) source->file->flags |= FLAG_IDL_HEADER;
if (!source->file->flags) return;
@@ -2839,9 +2829,10 @@
for (i = 0; i < ARRAY_SIZE(idl_outputs); i++)
{
if (!(source->file->flags & idl_outputs[i].flag)) continue;
+ multiarch = (make->use_msvcrt && archs.count > 1);
for (arch = 0; arch < archs.count; arch++)
{
- if (!is_multiarch( arch )) continue;
+ if (!arch != !multiarch) continue;
if (make->disabled[arch]) continue;
dest = strmake( "%s%s%s", arch_dirs[arch], obj, idl_outputs[i].ext );
if (!find_src_file( make, dest )) strarray_add( &make->clean_files, dest );
@@ -3152,13 +3143,13 @@
if (arch)
{
if (source->file->flags & FLAG_C_UNIX) return;
- if (!is_using_msvcrt( make ) && !make->staticlib && !(source->file->flags & FLAG_C_IMPLIB)) return;
+ if (!make->use_msvcrt && !make->staticlib && !(source->file->flags & FLAG_C_IMPLIB)) return;
}
else if (source->file->flags & FLAG_C_UNIX)
{
if (!unix_lib_supported) return;
}
- else if (archs.count > 1 && is_using_msvcrt( make ))
+ else if (archs.count > 1 && make->use_msvcrt)
{
if (!so_dll_supported) return;
if (!(source->file->flags & FLAG_C_IMPLIB) && (!make->staticlib || make->extlib)) return;
@@ -3349,6 +3340,12 @@
strarray_addall( &all_libs, add_import_libs( make, &dep_libs, default_imports, IMPORT_TYPE_DEFAULT, arch ) );
if (!arch) strarray_addall( &all_libs, libs );
+ if (!make->use_msvcrt)
+ {
+ strarray_addall( &all_libs, get_expanded_make_var_array( make, "UNIX_LIBS" ));
+ strarray_addall( &all_libs, libs );
+ }
+
if (delay_load_flags[arch])
{
for (i = 0; i < make->delayimports.count; i++)
@@ -3541,7 +3538,7 @@
output( ": %s", obj_dir_path( make, testmodule ));
if (parent)
{
- char *parent_module = arch_module_name( make->testdll, arch );
+ char *parent_module = arch_module_name( make->testdll, parent->use_msvcrt ? arch : 0 );
output_filename( obj_dir_path( parent, parent_module ));
if (parent->unixlib) output_filename( obj_dir_path( parent, parent->unixlib ));
}
@@ -3792,11 +3789,15 @@
}
else if (make->module)
{
- for (arch = 0; arch < archs.count; arch++)
+ if (!make->use_msvcrt) output_module( make, 0 );
+ else
{
- if (is_multiarch( arch )) output_module( make, arch );
- if (make->importlib && (is_multiarch( arch ) || !is_native_arch_disabled( make )))
- output_import_lib( make, arch );
+ for (arch = 0; arch < archs.count; arch++)
+ {
+ if (is_multiarch( arch )) output_module( make, arch );
+ if (make->importlib && (is_multiarch( arch ) || !is_native_arch_disabled( make )))
+ output_import_lib( make, arch );
+ }
}
if (make->unixlib) output_unix_lib( make );
if (make->is_exe && !make->is_win16 && unix_lib_supported && strendswith( make->module, ".exe" ))
@@ -4236,9 +4237,13 @@
}
make->is_win16 = strarray_exists( &make->extradllflags, "-m16" );
make->data_only = strarray_exists( &make->extradllflags, "-Wb,--data-only" );
+ make->use_msvcrt = (make->module || make->testdll || make->is_win16) &&
+ !strarray_exists( &make->extradllflags, "-mcygwin" );
make->is_exe = strarray_exists( &make->extradllflags, "-mconsole" ) ||
strarray_exists( &make->extradllflags, "-mwindows" );
+ if (make->use_msvcrt) strarray_add_uniq( &make->extradllflags, "-mno-cygwin" );
+
if (make->module)
{
/* add default install rules if nothing was specified */
@@ -4296,6 +4301,8 @@
add_generated_sources( make );
+ if (make->use_msvcrt) strarray_add( &make->define_args, get_crt_define( make ));
+
LIST_FOR_EACH_ENTRY( file, &make->includes, struct incl_file, entry ) parse_file( make, file, 0 );
LIST_FOR_EACH_ENTRY( file, &make->sources, struct incl_file, entry ) get_dependencies( file, file );

View file

@ -0,0 +1,145 @@
commit ebf411c1e5f20c6db7962cea587d6169246078e0
Author: Rémi Bernon <rbernon@codeweavers.com>
Date: Wed Jul 3 10:54:06 2019 +0200
Subject: [PATCH 2/3] server: Fallback to RTKIT for thread priorities.
sched_setscheduler and setpriority usually require elevated privileges
to succeed and most Linux distributions ship rtkit daemon with a dbus
interface to enable unprivileged control of some scheduling parameters.
--- a/configure.ac
+++ b/configure.ac
@@ -1416,7 +1416,7 @@
if test "x$with_dbus" != "xno"
then
WINE_PACKAGE_FLAGS(DBUS,[dbus-1],,,,
- [AC_CHECK_HEADER([dbus/dbus.h],
+ [AC_CHECK_HEADERS([dbus/dbus.h],
[WINE_CHECK_SONAME(dbus-1, dbus_connection_close,,[DBUS_CFLAGS=""],[$DBUS_LIBS])],
[DBUS_CFLAGS=""])])
fi
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -50,6 +50,7 @@
wineserver.man.in \
winstation.c
-UNIX_LIBS = $(LDEXECFLAGS) $(RT_LIBS) $(INOTIFY_LIBS) $(PROCSTAT_LIBS)
+UNIX_CFLAGS = $(DBUS_CFLAGS)
+UNIX_LIBS = $(LDEXECFLAGS) $(RT_LIBS) $(INOTIFY_LIBS) $(PROCSTAT_LIBS) $(DBUS_LIBS)
unicode_EXTRADEFS = -DNLSDIR="\"${nlsdir}\"" -DBIN_TO_NLSDIR=\"`${MAKEDEP} -R ${bindir} ${nlsdir}`\"
--- a/server/thread.c
+++ b/server/thread.c
@@ -59,6 +59,77 @@
#include "esync.h"
#include "fsync.h"
+#ifdef HAVE_DBUS_DBUS_H
+#include <dbus/dbus.h>
+
+static int rtkit_set_realtime( dbus_uint64_t process, dbus_uint64_t thread, dbus_uint32_t priority )
+{
+ DBusConnection* dbus;
+ DBusMessage *msg;
+ int ret = -1;
+
+ if ((dbus = dbus_bus_get(DBUS_BUS_SYSTEM, NULL)))
+ {
+ dbus_connection_set_exit_on_disconnect(dbus, 0);
+
+ if ((msg = dbus_message_new_method_call("org.freedesktop.RealtimeKit1",
+ "/org/freedesktop/RealtimeKit1",
+ "org.freedesktop.RealtimeKit1",
+ "MakeThreadRealtimeWithPID")))
+ {
+ dbus_message_set_no_reply(msg, 1);
+
+ if (dbus_message_append_args(msg,
+ DBUS_TYPE_UINT64, &process,
+ DBUS_TYPE_UINT64, &thread,
+ DBUS_TYPE_UINT32, &priority,
+ DBUS_TYPE_INVALID) &&
+ dbus_connection_send(dbus, msg, NULL))
+ ret = 0;
+
+ dbus_message_unref(msg);
+ }
+
+ dbus_connection_unref(dbus);
+ }
+
+ return ret;
+}
+
+static int rtkit_set_niceness( dbus_uint64_t process, dbus_uint64_t thread, dbus_int32_t niceness )
+{
+ DBusConnection* dbus;
+ DBusMessage *msg;
+ int ret = -1;
+
+ if ((dbus = dbus_bus_get(DBUS_BUS_SYSTEM, NULL)))
+ {
+ dbus_connection_set_exit_on_disconnect(dbus, 0);
+
+ if ((msg = dbus_message_new_method_call("org.freedesktop.RealtimeKit1",
+ "/org/freedesktop/RealtimeKit1",
+ "org.freedesktop.RealtimeKit1",
+ "MakeThreadHighPriorityWithPID")))
+ {
+ dbus_message_set_no_reply(msg, 1);
+
+ if (dbus_message_append_args(msg,
+ DBUS_TYPE_UINT64, &process,
+ DBUS_TYPE_UINT64, &thread,
+ DBUS_TYPE_INT32, &niceness,
+ DBUS_TYPE_INVALID) &&
+ dbus_connection_send(dbus, msg, NULL))
+ ret = 0;
+
+ dbus_message_unref(msg);
+ }
+
+ dbus_connection_unref(dbus);
+ }
+
+ return ret;
+}
+#endif
/* thread queues */
@@ -655,7 +726,8 @@
return mask;
}
-#if defined(HAVE_SCHED_SETSCHEDULER) || defined(HAVE_SETPRIORITY)
+#if defined(HAVE_SCHED_SETSCHEDULER) || defined(HAVE_SETPRIORITY) || \
+ defined(HAVE_DBUS_DBUS_H)
static int get_unix_priority( int priority_class, int priority )
{
switch (priority_class) {
@@ -778,6 +850,11 @@
if (sched_setscheduler( thread->unix_tid, SCHED_RR|SCHED_RESET_ON_FORK, &param ) == 0)
return 0;
#endif
+#ifdef HAVE_DBUS_DBUS_H
+ if (rtkit_set_realtime( thread->unix_pid, thread->unix_tid,
+ get_unix_priority( priority_class, priority ) ) == 0)
+ return 0;
+#endif
}
else
{
@@ -786,6 +863,11 @@
get_unix_priority( priority_class, priority ) ) == 0)
return 0;
#endif
+#ifdef HAVE_DBUS_DBUS_H
+ if (rtkit_set_niceness( thread->unix_pid, thread->unix_tid,
+ get_unix_priority( priority_class, priority ) ) == 0)
+ return 0;
+#endif
}
#endif

View file

@ -0,0 +1,30 @@
From df72c4d301123c0ea0c33af4bc9d00c47255a664 Mon Sep 17 00:00:00 2001
From: Torge Matthies <openglfreak@googlemail.com>
Date: Thu, 26 Jan 2023 22:13:55 +0100
Subject: [PATCH 3/3] server: Map THREAD_PRIORITY_IDLE to SCHED_IDLE.
---
server/thread.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
--- a/server/thread.c
+++ b/server/thread.c
@@ -858,6 +858,18 @@
}
else
{
+#ifdef HAVE_SCHED_SETSCHEDULER
+ if (priority == THREAD_PRIORITY_IDLE)
+ {
+ struct sched_param param;
+ if (sched_getparam( thread->unix_tid, &param ) == 0)
+ {
+ param.sched_priority = 0;
+ if (sched_setscheduler( thread->unix_tid, SCHED_IDLE|SCHED_RESET_ON_FORK, &param ) == 0)
+ return 0;
+ }
+ }
+#endif
#ifdef HAVE_SETPRIORITY
if (setpriority( PRIO_PROCESS, thread->unix_tid,
get_unix_priority( priority_class, priority ) ) == 0)

View file

@ -0,0 +1,12 @@
--- a/dlls/avrt/main.c
+++ b/dlls/avrt/main.c
@@ -70,6 +70,9 @@
return NULL;
}
+ if (!wcscmp(name, L"Audio") || !wcscmp(name, L"Pro Audio"))
+ SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);
+
return (HANDLE)0x12345678;
}

View file

@ -0,0 +1,36 @@
commit d444330ed7685686f46db7fb8ed1ad0cbec72c7b
Author: Rémi Bernon <rbernon@codeweavers.com>
Date: Wed Jun 16 17:36:15 2021 +0200
Subject: [PATCH] devenum: Register IEEE float for Direct Sound default device.
--
diff --git a/dlls/devenum/createdevenum.c b/dlls/devenum/createdevenum.c
index 8e9cf56eb09..97855b12b81 100644
--- a/dlls/devenum/createdevenum.c
+++ b/dlls/devenum/createdevenum.c
@@ -481,7 +481,7 @@ static BOOL CALLBACK register_dsound_devices(GUID *guid, const WCHAR *desc, cons
static const WCHAR defaultW[] = L"Default DirectSound Device";
IPropertyBag *prop_bag = NULL;
REGFILTERPINS2 rgpins = {0};
- REGPINTYPES rgtypes = {0};
+ REGPINTYPES rgtypes[2] = {};
REGFILTER2 rgf = {0};
WCHAR clsid[CHARS_IN_GUID];
VARIANT var;
@@ -512,10 +512,12 @@ static BOOL CALLBACK register_dsound_devices(GUID *guid, const WCHAR *desc, cons
rgf.rgPins2 = &rgpins;
rgpins.dwFlags = REG_PINFLAG_B_RENDERER;
/* FIXME: native registers many more formats */
- rgpins.nMediaTypes = 1;
- rgpins.lpMediaType = &rgtypes;
- rgtypes.clsMajorType = &MEDIATYPE_Audio;
- rgtypes.clsMinorType = &MEDIASUBTYPE_PCM;
+ rgpins.nMediaTypes = 2;
+ rgpins.lpMediaType = rgtypes;
+ rgtypes[0].clsMajorType = &MEDIATYPE_Audio;
+ rgtypes[0].clsMinorType = &MEDIASUBTYPE_PCM;
+ rgtypes[1].clsMajorType = &MEDIATYPE_Audio;
+ rgtypes[1].clsMinorType = &MEDIASUBTYPE_IEEE_FLOAT;
write_filter_data(prop_bag, &rgf);

View file

@ -0,0 +1,60 @@
From: Piotr Caban <piotr@codeweavers.com>
Subject: [PATCH v2] server: Don't wait for low level hook result when queuing hardware message.
Message-Id: <daf382d3-924e-7c33-c876-5b8d6298c137@codeweavers.com>
Date: Tue, 21 Sep 2021 15:51:35 +0200
Without the change graphic drivers are blocking until low level hooks
are processed when injecting keyboard and mouse events. Causes 2-seconds
(timeout) freeze in GtaV.
Signed-off-by: Piotr Caban <piotr@codeweavers.com>
---
v2:
- don't specify sender in send_hook_ll_message to avoid queuing result
server/queue.c | 16 +++++++++++++---
1 file changed, 13 insertions(+), 3 deletions(-)
diff --git a/server/queue.c b/server/queue.c
index e4903bcb79f..5c19348eeba 100644
--- a/server/queue.c
+++ b/server/queue.c
@@ -1839,7 +1839,12 @@ static int queue_mouse_message( struct desktop *desktop, user_handle_t win, cons
/* specify a sender only when sending the last message */
if (!(flags & ((1 << ARRAY_SIZE( messages )) - 1)))
{
- if (!(wait = send_hook_ll_message( desktop, msg, input, sender )))
+ if (origin == IMO_HARDWARE)
+ {
+ if (!send_hook_ll_message( desktop, msg, input, NULL ))
+ queue_hardware_message( desktop, msg, 0 );
+ }
+ else if (!(wait = send_hook_ll_message( desktop, msg, input, sender )))
queue_hardware_message( desktop, msg, 0 );
}
else if (!send_hook_ll_message( desktop, msg, input, NULL ))
@@ -1860,7 +1865,7 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c
struct thread *foreground;
unsigned char vkey = input->kbd.vkey;
unsigned int message_code, time;
- int wait;
+ int wait = 0;
if (!(time = input->kbd.time)) time = get_tick_count();
@@ -1981,7 +1986,12 @@ static int queue_keyboard_message( struct desktop *desktop, user_handle_t win, c
msg_data->flags |= (flags & (KF_EXTENDED | KF_ALTDOWN | KF_UP)) >> 8;
}
- if (!(wait = send_hook_ll_message( desktop, msg, input, sender )))
+ if (origin == IMO_HARDWARE)
+ {
+ if (!send_hook_ll_message( desktop, msg, input, NULL ))
+ queue_hardware_message( desktop, msg, 1 );
+ }
+ else if (!(wait = send_hook_ll_message( desktop, msg, input, sender )))
queue_hardware_message( desktop, msg, 1 );
return wait;

View file

@ -0,0 +1,55 @@
diff --git a/dlls/windowscodecs/converter.c b/dlls/windowscodecs/converter.c
index 11111111111..11111111111 100644
--- a/dlls/windowscodecs/converter.c
+++ b/dlls/windowscodecs/converter.c
@@ -1034,6 +1034,50 @@ static HRESULT copypixels_to_24bppBGR(struct FormatConverter *This, const WICRec
switch (source_format)
{
+ case format_8bppGray:
+ if (prc)
+ {
+ HRESULT res;
+ INT x, y;
+ BYTE *srcdata;
+ UINT srcstride, srcdatasize;
+ const BYTE *srcrow;
+ const BYTE *srcbyte;
+ BYTE *dstrow;
+ BYTE *dstpixel;
+
+ srcstride = prc->Width;
+ srcdatasize = srcstride * prc->Height;
+
+ srcdata = HeapAlloc(GetProcessHeap(), 0, srcdatasize);
+ if (!srcdata) return E_OUTOFMEMORY;
+
+ res = IWICBitmapSource_CopyPixels(This->source, prc, srcstride, srcdatasize, srcdata);
+
+ if (SUCCEEDED(res))
+ {
+ srcrow = srcdata;
+ dstrow = pbBuffer;
+ for (y=0; y<prc->Height; y++) {
+ srcbyte = srcrow;
+ dstpixel = dstrow;
+ for (x=0; x<prc->Width; x++)
+ {
+ *dstpixel++ = *srcbyte;
+ *dstpixel++ = *srcbyte;
+ *dstpixel++ = *srcbyte;
+ srcbyte++;
+ }
+ srcrow += srcstride;
+ dstrow += cbStride;
+ }
+ }
+
+ HeapFree(GetProcessHeap(), 0, srcdata);
+
+ return res;
+ }
+ return S_OK;
case format_24bppBGR:
case format_24bppRGB:
if (prc)

View file

@ -0,0 +1,54 @@
diff --git a/dlls/kernel32/module.c b/dlls/kernel32/module.c
index 11111111111..11111111111 100644
--- a/dlls/kernel32/module.c
+++ b/dlls/kernel32/module.c
@@ -262,6 +262,34 @@ BOOL WINAPI GetBinaryTypeA( LPCSTR lpApplicationName, LPDWORD lpBinaryType )
return GetBinaryTypeW(NtCurrentTeb()->StaticUnicodeString.Buffer, lpBinaryType);
}
+static BOOL block_wine_get_version = FALSE;
+
+BOOL CALLBACK init_block_wine_get_version( INIT_ONCE* init_once, PVOID param, PVOID *ctx )
+{
+ WCHAR *buffer;
+ DWORD size;
+
+ if ((size = GetEnvironmentVariableW( L"WINE_BLOCK_GET_VERSION", NULL, 0 )))
+ {
+ if (!(buffer = HeapAlloc( GetProcessHeap(), 0, sizeof(*buffer) * size )))
+ {
+ ERR("No memory.\n");
+ return FALSE;
+ }
+
+ if (GetEnvironmentVariableW( L"WINE_BLOCK_GET_VERSION", buffer, size ) != size - 1)
+ {
+ ERR("Error getting WINE_BLOCK_GET_VERSION env variable.\n");
+ return FALSE;
+ }
+
+ block_wine_get_version = *buffer && !!wcsncmp( buffer, L"0", 1 );
+
+ HeapFree( GetProcessHeap(), 0, buffer );
+ }
+ return TRUE;
+}
+
/***********************************************************************
* GetProcAddress (KERNEL32.@)
*
@@ -279,6 +307,14 @@ FARPROC get_proc_address( HMODULE hModule, LPCSTR function )
{
FARPROC fp;
+ if ((ULONG_PTR)function >> 16)
+ {
+ static INIT_ONCE init_once = INIT_ONCE_STATIC_INIT;
+ InitOnceExecuteOnce( &init_once, init_block_wine_get_version, NULL, NULL );
+ if (block_wine_get_version && !strncmp( function, "wine_get_version", 16 ))
+ return NULL;
+ }
+
if (!hModule) hModule = NtCurrentTeb()->Peb->ImageBaseAddress;
if ((ULONG_PTR)function >> 16)

View file

@ -0,0 +1,46 @@
diff --git a/dlls/ntdll/unix/sync.c b/dlls/ntdll/unix/sync.c
index e303367c29f..3723d21f885 100644
--- a/dlls/ntdll/unix/sync.c
+++ b/dlls/ntdll/unix/sync.c
@@ -2788,10 +2788,39 @@ NTSTATUS WINAPI NtDelayExecution( BOOLEAN alertable, const LARGE_INTEGER *timeou
}
else
{
+ LONGLONG ticks = timeout->QuadPart;
LARGE_INTEGER now;
- timeout_t when, diff;
+ timeout_t when = ticks, diff;
- if ((when = timeout->QuadPart) < 0)
+#if defined(HAVE_CLOCK_GETTIME) && defined(HAVE_CLOCK_NANOSLEEP)
+ static BOOL disable_clock_nanosleep = FALSE;
+ if (!disable_clock_nanosleep && ticks != 0)
+ {
+ struct timespec when;
+ int err;
+
+ if (ticks < 0)
+ {
+ clock_gettime( CLOCK_REALTIME, &when );
+ when.tv_sec += (time_t)(-ticks / TICKSPERSEC);
+ when.tv_nsec += (long)((-ticks % TICKSPERSEC) * 100);
+ }
+ else
+ {
+ when.tv_sec = (time_t)((ticks / TICKSPERSEC) - SECS_1601_TO_1970);
+ when.tv_nsec = (long)((ticks % TICKSPERSEC) * 100);
+ }
+
+ usleep(0);
+ while ((err = clock_nanosleep( CLOCK_REALTIME, TIMER_ABSTIME, &when, NULL )) == EINTR);
+ if (!err)
+ return STATUS_SUCCESS;
+ else
+ disable_clock_nanosleep = TRUE;
+ }
+#endif
+
+ if (when < 0)
{
NtQuerySystemTime( &now );
when = now.QuadPart - when;

View file

@ -0,0 +1,13 @@
## osu! fix: disables assertion causing game to crash when importing maps
diff --git a/dlls/ntdll/unix/thread.c b/dlls/ntdll/unix/thread.c
index 9e84ec3cc96..dfa2a2781bc 100644
--- a/dlls/ntdll/unix/thread.c
+++ b/dlls/ntdll/unix/thread.c
@@ -1783,7 +1783,6 @@ NTSTATUS get_thread_context( HANDLE handle, void *context, BOOL *self, USHORT ma
*/
void ntdll_set_exception_jmp_buf( __wine_jmp_buf *jmp )
{
- assert( !jmp || !ntdll_get_thread_data()->jmp_buf );
ntdll_get_thread_data()->jmp_buf = jmp;
}

View file

@ -0,0 +1,82 @@
diff --git a/dlls/win32u/sysparams.c b/dlls/win32u/sysparams.c
index 11111111111..11111111111 100644
--- a/dlls/win32u/sysparams.c
+++ b/dlls/win32u/sysparams.c
@@ -26,9 +26,14 @@
#include <pthread.h>
#include <assert.h>
+#include <stdarg.h>
#include "ntstatus.h"
#define WIN32_NO_STATUS
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+#include "ddk/wdm.h"
#include "ntgdi_private.h"
#include "ntuser_private.h"
#include "devpropdef.h"
@@ -1679,12 +1684,31 @@ static void clear_display_devices(void)
}
}
+static ULONGLONG last_update = 0;
+
+#define user_shared_data ((volatile const struct _KUSER_SHARED_DATA *)0x7ffe0000)
+
+static ULONGLONG get_tick_count(void)
+{
+ ULONG high, low;
+
+ do
+ {
+ high = user_shared_data->TickCount.High1Time;
+ low = user_shared_data->TickCount.LowPart;
+ }
+ while (high != user_shared_data->TickCount.High2Time);
+ /* note: we ignore TickCountMultiplier */
+ return (ULONGLONG)high << 32 | low;
+}
+
static BOOL update_display_cache_from_registry(void)
{
DWORD adapter_id, monitor_id, monitor_count = 0, size;
KEY_BASIC_INFORMATION key;
struct adapter *adapter;
struct monitor *monitor, *monitor2;
+ ULONGLONG tick_count;
HANDLE mutex = NULL;
NTSTATUS status;
BOOL ret;
@@ -1694,12 +1718,18 @@ static BOOL update_display_cache_from_registry(void)
sizeof(devicemap_video_keyW) )))
return FALSE;
+ if ((tick_count = get_tick_count()) - last_update < 1000) return TRUE;
+
status = NtQueryKey( video_key, KeyBasicInformation, &key,
offsetof(KEY_BASIC_INFORMATION, Name), &size );
if (status && status != STATUS_BUFFER_OVERFLOW)
return FALSE;
- if (key.LastWriteTime.QuadPart <= last_query_display_time) return TRUE;
+ if (key.LastWriteTime.QuadPart <= last_query_display_time)
+ {
+ last_update = tick_count;
+ return TRUE;
+ }
mutex = get_display_device_init_mutex();
pthread_mutex_lock( &display_lock );
@@ -1746,7 +1776,10 @@ static BOOL update_display_cache_from_registry(void)
}
if ((ret = !list_empty( &adapters ) && !list_empty( &monitors )))
+ {
last_query_display_time = key.LastWriteTime.QuadPart;
+ last_update = tick_count;
+ }
pthread_mutex_unlock( &display_lock );
release_display_device_init_mutex( mutex );
return ret;

View file

@ -0,0 +1,70 @@
From 138916577787bbdf19cf9523b15603f73b4f4472 Mon Sep 17 00:00:00 2001
From: Kai Krakow <kai@kaishome.de>
Date: Thu, 4 Oct 2018 05:51:20 +0200
Subject: [PATCH 2/2] winex11.drv: Bypass compositor in fullscreen mode.
Bypass the compositor in fullscreen mode. This reduces stutter
introduced by window updates in the background and also allows for maybe
a few more FPS. To not change the visual appearance of the desktop for
windowed games, this hack only enables itself when the game was switched
to fullscreen mode, and returns to default WM setting when the game
leaves fullscreen mode.
Compositors tend to cause severe stutter if the game is GPU-bound.
---
dlls/winex11.drv/window.c | 7 +++++++
dlls/winex11.drv/x11drv.h | 1 +
dlls/winex11.drv/x11drv_main.c | 1 +
3 files changed, 9 insertions(+)
--- a/dlls/winex11.drv/window.c
+++ b/dlls/winex11.drv/window.c
@@ -1036,6 +1036,7 @@
void update_net_wm_states( struct x11drv_win_data *data )
{
UINT i, style, ex_style, new_state = 0;
+ unsigned long net_wm_bypass_compositor = 0;
if (!data->managed) return;
if (data->whole_window == root_window) return;
@@ -1048,7 +1049,10 @@
if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION)
new_state |= (1 << NET_WM_STATE_MAXIMIZED);
else if (!(style & WS_MINIMIZE))
+ {
+ net_wm_bypass_compositor = 1;
new_state |= (1 << NET_WM_STATE_FULLSCREEN);
+ }
}
else if (style & WS_MAXIMIZE)
new_state |= (1 << NET_WM_STATE_MAXIMIZED);
@@ -1112,6 +1116,9 @@
}
data->net_wm_state = new_state;
update_net_wm_fullscreen_monitors( data );
+
+ XChangeProperty( data->display, data->whole_window, x11drv_atom(_NET_WM_BYPASS_COMPOSITOR), XA_CARDINAL,
+ 32, PropModeReplace, (unsigned char *)&net_wm_bypass_compositor, 1 );
}
/***********************************************************************
--- a/dlls/winex11.drv/x11drv.h
+++ b/dlls/winex11.drv/x11drv.h
@@ -501,6 +501,7 @@
XATOM__NET_SYSTEM_TRAY_OPCODE,
XATOM__NET_SYSTEM_TRAY_S0,
XATOM__NET_SYSTEM_TRAY_VISUAL,
+ XATOM__NET_WM_BYPASS_COMPOSITOR,
XATOM__NET_WM_FULLSCREEN_MONITORS,
XATOM__NET_WM_ICON,
XATOM__NET_WM_MOVERESIZE,
--- a/dlls/winex11.drv/x11drv_main.c
+++ b/dlls/winex11.drv/x11drv_main.c
@@ -159,6 +159,7 @@
"_NET_SYSTEM_TRAY_OPCODE",
"_NET_SYSTEM_TRAY_S0",
"_NET_SYSTEM_TRAY_VISUAL",
+ "_NET_WM_BYPASS_COMPOSITOR",
"_NET_WM_FULLSCREEN_MONITORS",
"_NET_WM_ICON",
"_NET_WM_MOVERESIZE",

View file

@ -0,0 +1,28 @@
From 5c8c060fc9d1d20eebe12da2e6dacddd88c07d82 Mon Sep 17 00:00:00 2001
From: Torge Matthies <tmatthies@codeweavers.com>
Date: Sun, 26 Nov 2023 18:29:53 +0100
Subject: [PATCH] HACK: Fix osu! alt-tab.
---
dlls/win32u/window.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/dlls/win32u/window.c b/dlls/win32u/window.c
index 11111111111..11111111111 100644
--- a/dlls/win32u/window.c
+++ b/dlls/win32u/window.c
@@ -3514,6 +3514,11 @@ BOOL set_window_pos( WINDOWPOS *winpos, int parent_x, int parent_y )
orig_flags = winpos->flags;
+ /* HACK: fix osu! taking back focus immediately when it is unfocused. */
+ if (winpos->hwndInsertAfter == HWND_NOTOPMOST &&
+ (get_window_long( winpos->hwnd, GWL_EXSTYLE ) & WS_EX_TOPMOST))
+ winpos->flags |= SWP_NOACTIVATE | SWP_NOZORDER;
+
/* First, check z-order arguments. */
if (!(winpos->flags & SWP_NOZORDER))
{
--
0.0.0

View file

@ -0,0 +1,324 @@
From ec87cc3742130c138e4caa37084c92c46b9cb9ad Mon Sep 17 00:00:00 2001
From: Torge Matthies <openglfreak@googlemail.com>
Date: Sun, 3 Jul 2022 15:54:01 +0200
Subject: [PATCH 2/2] winex11.drv: Add OpenGL latency reduction code.
---
dlls/winex11.drv/opengl.c | 255 +++++++++++++++++++++++++++++++++++++-
1 file changed, 252 insertions(+), 3 deletions(-)
diff --git a/dlls/winex11.drv/opengl.c b/dlls/winex11.drv/opengl.c
index 11111111111..11111111111 100644
--- a/dlls/winex11.drv/opengl.c
+++ b/dlls/winex11.drv/opengl.c
@@ -42,6 +42,8 @@
#include "xcomposite.h"
#include "winternl.h"
#include "wine/debug.h"
+#include "wine/server.h"
+#include "../win32u/ntuser_private.h"
#ifdef SONAME_LIBGL
@@ -225,6 +229,8 @@ enum dc_gl_layered_type
DC_GL_LAYERED_ATTRIBUTES,
};
+typedef LONGLONG rtime_t;
+
struct gl_drawable
{
LONG ref; /* reference count */
@@ -3443,6 +3454,130 @@ static void X11DRV_WineGL_LoadExtensions(void)
}
}
+static inline BOOL allow_latency_reduction( void )
+{
+ static int status = -1;
+ if (status == -1)
+ {
+ const char *env = getenv( "WINE_OPENGL_LATENCY_REDUCTION" );
+ status = !!(env && atoi(env));
+ }
+ return status == 1;
+}
+
+#define TICKSPERSEC 10000000
+
+typedef struct ftime_t {
+ LONGLONG time;
+ ULONGLONG freq;
+} ftime_t;
+
+static inline ftime_t current_ftime( void )
+{
+ LARGE_INTEGER counter, freq;
+ ftime_t ret;
+ NtQueryPerformanceCounter( &counter, &freq );
+ ret.time = counter.QuadPart;
+ ret.freq = (ULONGLONG)freq.QuadPart;
+ return ret;
+}
+
+static inline rtime_t ftime_to_rtime( ftime_t ftime, BOOL round_up )
+{
+ ftime.time *= TICKSPERSEC;
+ if (round_up)
+ ftime.time += ftime.freq - 1;
+ return ftime.time / ftime.freq;
+}
+
+static inline rtime_t current_rtime( BOOL round_up )
+{
+ return ftime_to_rtime( current_ftime(), round_up );
+}
+
+static rtime_t get_vblank_interval( HWND hwnd )
+{
+ HMONITOR monitor;
+ UNICODE_STRING device_name;
+ MONITORINFOEXW moninfo = { sizeof(MONITORINFOEXW) };
+ DEVMODEW devmode = { {0}, 0, 0, sizeof(DEVMODEW) };
+
+ monitor = NtUserMonitorFromWindow( hwnd, MONITOR_DEFAULTTONEAREST );
+ if (!monitor || !NtUserGetMonitorInfo( monitor, (MONITORINFO*)&moninfo ))
+ return 0;
+
+ RtlInitUnicodeString( &device_name, moninfo.szDevice );
+ if (!NtUserEnumDisplaySettings( &device_name, ENUM_CURRENT_SETTINGS, &devmode, 0 )
+ || devmode.dmDisplayFrequency <= 1)
+ return 0;
+ MESSAGE("detected display frequency: %u\n", devmode.dmDisplayFrequency);
+ return TICKSPERSEC / devmode.dmDisplayFrequency;
+}
+
+#define FRAMETIME_MARGIN_SHIFT 2
+
+static inline rtime_t frame_time_with_margin( rtime_t frame_time )
+{
+ return frame_time + (frame_time >> FRAMETIME_MARGIN_SHIFT) + 3500;
+}
+
+static void get_swap_interval(GLXDrawable drawable, int *interval)
+{
+ /* HACK: does not work correctly with __GL_SYNC_TO_VBLANK */
+ /*pglXQueryDrawable(gdi_display, gl->drawable, GLX_SWAP_INTERVAL_EXT, (unsigned int*)interval);*/
+ *interval = 0;
+}
+
+#define WAIT_MASK (QS_MOUSEBUTTON | QS_KEY | QS_SENDMESSAGE | QS_TIMER | QS_HOTKEY)
+
+static void msg_wait( const LARGE_INTEGER *timeout )
+{
+ LARGE_INTEGER to = *timeout, to2 = to;
+ rtime_t start, end;
+ DWORD ret;
+
+ /* HACK: __wine_msg_wait_objects likes to wait for about 1 ms too long */
+
+ if (to2.QuadPart < 0)
+ {
+ to2.QuadPart += 10000;
+ if (to2.QuadPart >= 0)
+ {
+ end = current_rtime( TRUE );
+ goto busy_loop;
+ }
+ }
+ else if (to2.QuadPart >= 10000)
+ to2.QuadPart -= 10000;
+
+ if (to2.QuadPart >= 0)
+ {
+ __wine_msg_wait_objects( 0, NULL, &to2, WAIT_MASK, MWMO_INPUTAVAILABLE );
+ return;
+ }
+
+again:
+ start = current_rtime( FALSE );
+ ret = __wine_msg_wait_objects( 0, NULL, &to2, WAIT_MASK, MWMO_INPUTAVAILABLE );
+ if (ret == WAIT_OBJECT_0)
+ return;
+ end = current_rtime( TRUE );
+
+ to.QuadPart += end - start;
+ if (to.QuadPart < -11000)
+ {
+ to2.QuadPart = to.QuadPart + 10000;
+ goto again;
+ }
+
+busy_loop:
+ if (to.QuadPart < -1000)
+ {
+ end = end - to.QuadPart - 1000;
+ while (current_rtime( TRUE ) < end)
+ YieldProcessor();
+ }
+}
/**
* glxdrv_SwapBuffers
@@ -3457,6 +3592,11 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc )
INT64 ust, msc, sbc, target_sbc = 0;
HWND hwnd;
+ BOOL enable_latency_reduction = FALSE;
+ BOOL synchronize_to_vblank = FALSE;
+ rtime_t frame_end_time;
+ rtime_t next_vblank_time = 0;
+
TRACE("(%p)\n", hdc);
escape.code = X11DRV_PRESENT_DRAWABLE;
@@ -3469,18 +3609,78 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc )
return FALSE;
}
+ if (allow_latency_reduction())
+ enable_latency_reduction = gl->type == DC_GL_WINDOW
+ || gl->type == DC_GL_CHILD_WIN || gl->type == DC_GL_PIXMAP_WIN;
+
+ if (enable_latency_reduction)
+ {
+ if (ctx && (gl->type == DC_GL_WINDOW || gl->type == DC_GL_CHILD_WIN
+ || gl->type == DC_GL_PIXMAP_WIN))
+ sync_context( ctx );
+ pglFinish();
+ frame_end_time = current_rtime( TRUE );
+ }
+
pthread_mutex_lock( &context_mutex );
- if (gl->refresh_swap_interval)
+
+ if (enable_latency_reduction)
+ {
+ if (!gl->vblank_interval)
+ {
+ HWND hwnd = 0;
+ assert(!XFindContext( gdi_display, gl->window, winContext, (char **)&hwnd ));
+ assert(hwnd);
+ gl->vblank_interval = get_vblank_interval( hwnd );
+ assert(gl->vblank_interval);
+ }
+
+ if (gl->last_vblank_time)
+ {
+ next_vblank_time = gl->last_vblank_time + gl->vblank_interval;
+ while (next_vblank_time < frame_end_time)
+ next_vblank_time += gl->vblank_interval;
+ }
+
+ if (gl->last_swap_time)
+ {
+ rtime_t new_frame_time = frame_end_time - gl->last_swap_time;
+ if (new_frame_time >= gl->frame_time)
+ gl->frame_time = new_frame_time;
+ else if (gl->frame_time > new_frame_time * 3)
+ gl->frame_time = frame_time_with_margin( new_frame_time );
+ else
+ gl->frame_time = (gl->frame_time * 20 + new_frame_time) / 21;
+ }
+
+ if (frame_end_time - gl->last_vblank_time >= TICKSPERSEC
+ || (!gl->refresh_swap_interval && next_vblank_time - frame_end_time <= frame_time_with_margin( gl->frame_time )))
+ synchronize_to_vblank = TRUE;
+ }
+
+ if (synchronize_to_vblank)
+ {
+ if (!gl->previous_frame_synchronized)
+ {
+ get_swap_interval(gl->drawable, &gl->swap_interval);
+ if (!set_swap_interval(gl->drawable, 1))
+ synchronize_to_vblank = FALSE;
+ gl->previous_frame_synchronized = TRUE;
+ }
+ }
+ else if (gl->refresh_swap_interval || gl->previous_frame_synchronized)
{
set_swap_interval(gl->drawable, gl->swap_interval);
gl->refresh_swap_interval = FALSE;
+ gl->previous_frame_synchronized = FALSE;
}
+
pthread_mutex_unlock( &context_mutex );
switch (gl->type)
{
case DC_GL_PIXMAP_WIN:
- if (ctx) sync_context( ctx );
+ if (!enable_latency_reduction && ctx) sync_context( ctx );
escape.drawable = gl->pixmap;
if (pglXCopySubBufferMESA) {
/* (glX)SwapBuffers has an implicit glFlush effect, however
@@ -3501,7 +3701,7 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc )
break;
case DC_GL_WINDOW:
case DC_GL_CHILD_WIN:
- if (ctx) sync_context( ctx );
+ if (!enable_latency_reduction && ctx) sync_context( ctx );
if (gl->type == DC_GL_CHILD_WIN) escape.drawable = gl->window;
/* fall through */
default:
@@ -3519,5 +3719,54 @@ static BOOL glxdrv_wglSwapBuffers( HDC hdc )
pglXWaitForSbcOML( gdi_display, gl->drawable, target_sbc, &ust, &msc, &sbc );
+
+ if (enable_latency_reduction)
+ {
+ rtime_t current_time = current_rtime( FALSE );
+
+ if (!synchronize_to_vblank && gl->last_vblank_time && gl->frame_time)
+ {
+ LARGE_INTEGER timeout;
+
+ next_vblank_time = gl->last_vblank_time + gl->vblank_interval;
+ while (next_vblank_time < current_time + frame_time_with_margin( gl->frame_time ))
+ next_vblank_time += gl->vblank_interval;
+
+ timeout.QuadPart = -(next_vblank_time - frame_time_with_margin( gl->frame_time ) - current_time);
+ if (timeout.QuadPart < 0 && -timeout.QuadPart < TICKSPERSEC)
+ msg_wait( &timeout );
+
+ current_time = current_rtime( FALSE );
+ }
+
+ pthread_mutex_lock( &context_mutex );
+
+ gl->last_swap_time = current_time;
+ if (synchronize_to_vblank)
+ gl->last_vblank_time = current_time;
+
+ pthread_mutex_unlock( &context_mutex );
+
+ if (synchronize_to_vblank && gl->frame_time)
+ {
+ LARGE_INTEGER timeout;
+
+ next_vblank_time = gl->last_vblank_time + gl->vblank_interval;
+ while (next_vblank_time < current_time + frame_time_with_margin( gl->frame_time ))
+ next_vblank_time += gl->vblank_interval;
+
+ timeout.QuadPart = -(next_vblank_time - frame_time_with_margin( gl->frame_time ) - current_time);
+ if (timeout.QuadPart < 0 && -timeout.QuadPart < TICKSPERSEC)
+ {
+ msg_wait( &timeout );
+
+ current_time = current_rtime( FALSE );
+ pthread_mutex_lock( &context_mutex );
+ gl->last_swap_time = current_time;
+ pthread_mutex_unlock( &context_mutex );
+ }
+ }
+ }
+
release_gl_drawable( gl );
if (ctx && escape.drawable)
--
2.40.0

View file

@ -0,0 +1,319 @@
From a21d85ace24116af87b83738909001c1e7cf87c2 Mon Sep 17 00:00:00 2001
From: Torge Matthies <tmatthies@codeweavers.com>
Date: Wed, 23 Nov 2022 15:47:49 +0100
Subject: [PATCH 1/3] ntdll: Add MADV_DONTNEED-based implementation of
NtFlushProcessWriteBuffers.
Credits to Avi Kivity (scylladb) and Aliaksei Kandratsenka (gperftools) for this trick, see [1].
[1] https://github.com/scylladb/seastar/commit/77a58e4dc020233f66fccb8d9e8f7a8b7f9210c4
---
dlls/ntdll/unix/virtual.c | 52 +++++++++++++++++++++++++++++++++++++-
tools/winapi/nativeapi.dat | 1 +
2 files changed, 52 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c
index 8087a12785c..de8f8b6ebc1 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -215,6 +215,11 @@ struct range_entry
static struct range_entry *free_ranges;
static struct range_entry *free_ranges_end;
+#if defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
+static void *dontneed_page;
+static pthread_mutex_t dontneed_page_mutex = PTHREAD_MUTEX_INITIALIZER;
+#endif
+
static inline BOOL is_beyond_limit( const void *addr, size_t size, const void *limit )
{
@@ -5174,14 +5179,58 @@ NTSTATUS WINAPI NtFlushInstructionCache( HANDLE handle, const void *addr, SIZE_T
}
+static BOOL try_madvise( void )
+{
+ BOOL success = FALSE;
+ char *mem;
+
+ pthread_mutex_lock(&dontneed_page_mutex);
+ /* Credits to Avi Kivity (scylladb) and Aliaksei Kandratsenka (gperftools) for this trick,
+ see https://github.com/scylladb/seastar/commit/77a58e4dc020233f66fccb8d9e8f7a8b7f9210c4 */
+ mem = dontneed_page;
+ if (!mem)
+ {
+ int ret;
+ /* Allocate one page of memory that we can call madvise() on */
+ mem = anon_mmap_alloc( page_size, PROT_READ | PROT_WRITE );
+ if (mem == MAP_FAILED)
+ goto failed;
+ /* If the memory is locked, e.g. by a call to mlockall(MCL_FUTURE), the madvise() call below
+ will fail with error EINVAL, so unlock it here */
+ ret = munlock( mem, page_size );
+ /* munlock() may fail on old kernels if we don't have sufficient permissions, but that is not
+ a problem since in that case we didn't have permission to lock the memory either */
+ if (ret && errno != EPERM)
+ goto failed;
+ dontneed_page = mem;
+ }
+ /* Force the page into memory to make madvise() have real work to do */
+ *mem = 3;
+ /* Evict the page from memory to force the kernel to send an IPI to all threads of this process,
+ which has the side effect of executing a memory barrier in those threads */
+ success = !madvise( mem, page_size, MADV_DONTNEED );
+failed:
+ pthread_mutex_unlock(&dontneed_page_mutex);
+ return success;
+}
+
+
/**********************************************************************
* NtFlushProcessWriteBuffers (NTDLL.@)
*/
NTSTATUS WINAPI NtFlushProcessWriteBuffers(void)
{
static int once = 0;
- if (!once++) FIXME( "stub\n" );
- return STATUS_SUCCESS;
+ if (try_madvise())
+ {
+#ifdef __aarch64__
+ /* Some ARMv8 processors can broadcast TLB invalidations using the TLBI instruction,
+ the madvise trick does not work on those */
+ if (!once++) FIXME( "memory barrier may not work on this platform\n" );
+#endif
+ return;
+ }
+ if (!once++) FIXME( "no implementation available on this platform\n" );
}
diff --git a/tools/winapi/nativeapi.dat b/tools/winapi/nativeapi.dat
index ade20b5ee68..5512c4f1833 100644
--- a/tools/winapi/nativeapi.dat
+++ b/tools/winapi/nativeapi.dat
@@ -134,6 +134,7 @@ log10
logb
longjmp
lseek
+madvise
malloc
mblen
memccpy
--
GitLab
From d3afd6ff2ffe7942d6e0846dea52a3884111a06a Mon Sep 17 00:00:00 2001
From: Torge Matthies <tmatthies@codeweavers.com>
Date: Wed, 23 Nov 2022 15:47:50 +0100
Subject: [PATCH 2/3] ntdll: Add sys_membarrier-based implementation of
NtFlushProcessWriteBuffers.
Uses the MEMBARRIER_CMD_PRIVATE_EXPEDITED membarrier command introduced in Linux 4.14.
---
dlls/ntdll/unix/virtual.c | 49 ++++++++++++++++++++++++++++++++++++++-
1 file changed, 48 insertions(+), 1 deletion(-)
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c
index de8f8b6ebc1..e90bdb3abfb 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -39,6 +39,9 @@
#ifdef HAVE_SYS_SYSINFO_H
# include <sys/sysinfo.h>
#endif
+#ifdef HAVE_SYS_SYSCALL_H
+# include <sys/syscall.h>
+#endif
#ifdef HAVE_SYS_SYSCTL_H
# include <sys/sysctl.h>
#endif
@@ -215,10 +218,16 @@ struct range_entry
static struct range_entry *free_ranges;
static struct range_entry *free_ranges_end;
-#if defined(__linux__) && (defined(__i386__) || defined(__x86_64__))
+#ifdef __linux__
+#ifdef __NR_membarrier
+static BOOL membarrier_exp_available;
+static pthread_once_t membarrier_init_once = PTHREAD_ONCE_INIT;
+#endif
+#if defined(__i386__) || defined(__x86_64__)
static void *dontneed_page;
static pthread_mutex_t dontneed_page_mutex = PTHREAD_MUTEX_INITIALIZER;
#endif
+#endif
static inline BOOL is_beyond_limit( const void *addr, size_t size, const void *limit )
@@ -5179,6 +5188,42 @@ NTSTATUS WINAPI NtFlushInstructionCache( HANDLE handle, const void *addr, SIZE_T
}
+#if defined(__linux__) && defined(__NR_membarrier)
+#define MEMBARRIER_CMD_QUERY 0x00
+#define MEMBARRIER_CMD_PRIVATE_EXPEDITED 0x08
+#define MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED 0x10
+
+
+static int membarrier( int cmd, unsigned int flags, int cpu_id )
+{
+ return syscall( __NR_membarrier, cmd, flags, cpu_id );
+}
+
+
+static void membarrier_init( void )
+{
+ static const int exp_required_cmds =
+ MEMBARRIER_CMD_PRIVATE_EXPEDITED | MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED;
+ int available_cmds = membarrier( MEMBARRIER_CMD_QUERY, 0, 0 );
+ if (available_cmds == -1)
+ return;
+ if ((available_cmds & exp_required_cmds) == exp_required_cmds)
+ membarrier_exp_available = !membarrier( MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED, 0, 0 );
+}
+
+
+static BOOL try_exp_membarrier( void )
+{
+ pthread_once(&membarrier_init_once, membarrier_init);
+ if (!membarrier_exp_available)
+ return FALSE;
+ return !membarrier( MEMBARRIER_CMD_PRIVATE_EXPEDITED, 0, 0 );
+}
+#else
+static BOOL try_exp_membarrier( void ) { return 0; }
+#endif
+
+
static BOOL try_madvise( void )
{
BOOL success = FALSE;
@@ -5221,6 +5266,8 @@ failed:
void WINAPI NtFlushProcessWriteBuffers(void)
{
static int once = 0;
+ if (try_exp_membarrier())
+ return;
if (try_madvise())
{
#ifdef __aarch64__
--
GitLab
From 48f1d7cad78235c5c9e64c419235289608294440 Mon Sep 17 00:00:00 2001
From: Torge Matthies <tmatthies@codeweavers.com>
Date: Wed, 23 Nov 2022 15:47:50 +0100
Subject: [PATCH 3/3] ntdll: Add thread_get_register_pointer_values-based
implementation of NtFlushProcessWriteBuffers.
---
dlls/ntdll/unix/virtual.c | 68 +++++++++++++++++++++++++++++++++++++++
1 file changed, 68 insertions(+)
diff --git a/dlls/ntdll/unix/virtual.c b/dlls/ntdll/unix/virtual.c
index e90bdb3abfb..c5a2f878e3b 100644
--- a/dlls/ntdll/unix/virtual.c
+++ b/dlls/ntdll/unix/virtual.c
@@ -65,6 +65,9 @@
#if defined(__APPLE__)
# include <mach/mach_init.h>
# include <mach/mach_vm.h>
+# include <mach/task.h>
+# include <mach/thread_state.h>
+# include <mach/vm_map.h>
#endif
#include "ntstatus.h"
@@ -218,6 +221,11 @@ struct range_entry
static struct range_entry *free_ranges;
static struct range_entry *free_ranges_end;
+#ifdef __APPLE__
+static kern_return_t (*p_thread_get_register_pointer_values)( thread_t, uintptr_t*, size_t*, uintptr_t* );
+static pthread_once_t tgrpvs_init_once = PTHREAD_ONCE_INIT;
+#endif
+
#ifdef __linux__
#ifdef __NR_membarrier
static BOOL membarrier_exp_available;
@@ -5188,6 +5196,64 @@ NTSTATUS WINAPI NtFlushInstructionCache( HANDLE handle, const void *addr, SIZE_T
}
+#ifdef __APPLE__
+
+static void tgrpvs_init( void )
+{
+ p_thread_get_register_pointer_values = dlsym( RTLD_DEFAULT, "thread_get_register_pointer_values" );
+}
+
+static BOOL try_mach_tgrpvs( void )
+{
+ /* Taken from https://github.com/dotnet/runtime/blob/7be37908e5a1cbb83b1062768c1649827eeaceaa/src/coreclr/pal/src/thread/process.cpp#L2799 */
+ mach_msg_type_number_t count, i = 0;
+ thread_act_array_t threads;
+ kern_return_t kret;
+ BOOL success = FALSE;
+
+ pthread_once(&tgrpvs_init_once, tgrpvs_init);
+ if (!p_thread_get_register_pointer_values)
+ return FALSE;
+
+ /* Get references to all threads of this process */
+ kret = task_threads( mach_task_self(), &threads, &count );
+ if (kret)
+ return FALSE;
+
+ /* Iterate through the threads in the list */
+ while (i < count)
+ {
+ uintptr_t reg_values[128];
+ size_t reg_count = ARRAY_SIZE( reg_values );
+ uintptr_t sp;
+
+ /* Request the thread's register pointer values to force the thread to go through a memory barrier */
+ kret = p_thread_get_register_pointer_values( threads[i], &sp, &reg_count, reg_values );
+ /* This function always fails when querying Rosetta's exception handling thread, so we only treat
+ KERN_INSUFFICIENT_BUFFER_SIZE as an error, like .NET core does. */
+ if (kret == KERN_INSUFFICIENT_BUFFER_SIZE)
+ goto fail;
+
+ /* Deallocate thread reference once we're done with it */
+ kret = mach_port_deallocate( mach_task_self(), threads[i++] );
+ if (kret)
+ goto fail;
+ }
+ success = TRUE;
+fail:
+ /* Deallocate remaining thread references */
+ while (i < count)
+ mach_port_deallocate( mach_task_self(), threads[i++] );
+ /* Deallocate thread list */
+ vm_deallocate( mach_task_self(), (vm_address_t)threads, count * sizeof(threads[0]) );
+ return success;
+}
+
+#else
+static BOOL try_mach_tgrpvs( void ) { return 0; }
+#endif
+
+
#if defined(__linux__) && defined(__NR_membarrier)
#define MEMBARRIER_CMD_QUERY 0x00
#define MEMBARRIER_CMD_PRIVATE_EXPEDITED 0x08
@@ -5266,6 +5332,8 @@ failed:
void WINAPI NtFlushProcessWriteBuffers(void)
{
static int once = 0;
+ if (try_mach_tgrpvs())
+ return;
if (try_exp_membarrier())
return;
if (try_madvise())
--
GitLab

View file

@ -0,0 +1,27 @@
## Patch to revert commit 35193586 which supposedly broke osu!
## on GNOME on X11 due to compositor not turning off even with fullscreen enabled
--- b/dlls/winex11.drv/window.c
+++ a/dlls/winex11.drv/window.c
@@ -1542,21 +1542,9 @@
attrib.border_pixel = 0;
attrib.colormap = default_colormap;
-#ifdef HAVE_LIBXSHAPE
- {
- static XRectangle empty_rect;
- dummy_parent = XCreateWindow( gdi_display, root_window, 0, 0, 1, 1, 0,
- default_visual.depth, InputOutput, default_visual.visual,
- CWColormap | CWBorderPixel | CWOverrideRedirect, &attrib );
- XShapeCombineRectangles( gdi_display, dummy_parent, ShapeBounding, 0, 0, &empty_rect, 1,
- ShapeSet, YXBanded );
- }
-#else
dummy_parent = XCreateWindow( gdi_display, root_window, -1, -1, 1, 1, 0, default_visual.depth,
InputOutput, default_visual.visual,
CWColormap | CWBorderPixel | CWOverrideRedirect, &attrib );
- WARN("Xshape support is not compiled in. Applications under XWayland may have poor performance.\n");
-#endif
XMapWindow( gdi_display, dummy_parent );
}
return dummy_parent;

177
pkgs/osu-wine/sources.nix Normal file
View file

@ -0,0 +1,177 @@
{ pkgs ? import <nixpkgs> {}, path ? pkgs.path }:
## we default to importing <nixpkgs> here, so that you can use
## a simple shell command to insert new hashes into this file
## e.g. with emacs C-u M-x shell-command
##
## nix-prefetch-url sources.nix -A {stable{,.mono,.gecko64,.gecko32}, unstable, staging, winetricks}
# here we wrap fetchurl and fetchFromGitHub, in order to be able to pass additional args around it
let fetchurl = args@{url, hash, ...}:
pkgs.fetchurl { inherit url hash; } // args;
fetchFromGitHub = args@{owner, repo, rev, hash, ...}:
pkgs.fetchFromGitHub { inherit owner repo rev hash; } // args;
fetchFromGitLab = args@{domain, owner, repo, rev, hash, ...}:
pkgs.fetchFromGitLab { inherit domain owner repo rev hash; } // args;
updateScriptPreamble = ''
set -eou pipefail
PATH=${with pkgs; lib.makeBinPath [ common-updater-scripts coreutils curl gnugrep gnused jq nix ]}
sources_file=${__curPos.file}
source ${./update-lib.sh}
'';
inherit (pkgs) writeShellScript;
in rec {
stable = fetchurl rec {
version = "9.0";
url = "https://dl.winehq.org/wine/source/9.0/wine-${version}.tar.xz";
hash = "sha256-fP0JClOV9bdtlbtd76yKMSyN5MBwwRY7i1jaODMMpu4=";
## see http://wiki.winehq.org/Gecko
gecko32 = fetchurl rec {
version = "2.47.4";
url = "https://dl.winehq.org/wine/wine-gecko/${version}/wine-gecko-${version}-x86.msi";
hash = "sha256-Js7MR3BrCRkI9/gUvdsHTGG+uAYzGOnvxaf3iYV3k9Y=";
};
gecko64 = fetchurl rec {
version = "2.47.4";
url = "https://dl.winehq.org/wine/wine-gecko/${version}/wine-gecko-${version}-x86_64.msi";
hash = "sha256-5ZC32YijLWqkzx2Ko6o9M3Zv3Uz0yJwtzCCV7LKNBm8=";
};
## see http://wiki.winehq.org/Mono
mono = fetchurl rec {
version = "8.1.0";
url = "https://dl.winehq.org/wine/wine-mono/${version}/wine-mono-${version}-x86.msi";
hash = "sha256-DtPsUzrvebLzEhVZMc97EIAAmsDFtMK8/rZ4rJSOCBA=";
};
patches = [
# Also look for root certificates at $NIX_SSL_CERT_FILE
"${path}/pkgs/applications/emulators/wine/cert-path.patch"
];
updateScript = writeShellScript "update-wine-stable" (''
${updateScriptPreamble}
major=''${UPDATE_NIX_OLD_VERSION%%.*}
latest_stable=$(get_latest_wine_version "$major.0")
# Can't use autobump on stable because we don't want the path
# <source/7.0/wine-7.0.tar.xz> to become <source/7.0.1/wine-7.0.1.tar.xz>.
if [[ "$UPDATE_NIX_OLD_VERSION" != "$latest_stable" ]]; then
set_version_and_hash stable "$latest_stable" "$(nix-prefetch-url "$wine_url_base/source/$major.0/wine-$latest_stable.tar.xz")"
fi
do_update
'');
};
unstable = fetchurl rec {
# NOTE: Don't forget to change the hash for staging as well.
version = "9.0";
url = "https://dl.winehq.org/wine/source/9.0/wine-${version}.tar.xz";
hash = "sha256-fP0JClOV9bdtlbtd76yKMSyN5MBwwRY7i1jaODMMpu4=";
inherit (stable) patches;
## see http://wiki.winehq.org/Gecko
gecko32 = fetchurl rec {
version = "2.47.4";
url = "https://dl.winehq.org/wine/wine-gecko/${version}/wine-gecko-${version}-x86.msi";
hash = "sha256-Js7MR3BrCRkI9/gUvdsHTGG+uAYzGOnvxaf3iYV3k9Y=";
};
gecko64 = fetchurl rec {
version = "2.47.4";
url = "https://dl.winehq.org/wine/wine-gecko/${version}/wine-gecko-${version}-x86_64.msi";
hash = "sha256-5ZC32YijLWqkzx2Ko6o9M3Zv3Uz0yJwtzCCV7LKNBm8=";
};
## see http://wiki.winehq.org/Mono
mono = fetchurl rec {
version = "8.1.0";
url = "https://dl.winehq.org/wine/wine-mono/${version}/wine-mono-${version}-x86.msi";
hash = "sha256-DtPsUzrvebLzEhVZMc97EIAAmsDFtMK8/rZ4rJSOCBA=";
};
updateScript = writeShellScript "update-wine-unstable" ''
${updateScriptPreamble}
major=''${UPDATE_NIX_OLD_VERSION%%.*}
latest_unstable=$(get_latest_wine_version "$major.x")
latest_gecko=$(get_latest_lib_version wine-gecko)
latest_mono=$(get_latest_lib_version wine-mono)
update_staging() {
staging_url=$(get_source_attr staging.url)
set_source_attr staging hash "\"$(to_sri "$(nix-prefetch-url --unpack "''${staging_url//$1/$2}")")\""
}
autobump unstable "$latest_unstable" "" update_staging
autobump unstable.gecko32 "$latest_gecko"
autobump unstable.gecko64 "$latest_gecko"
autobump unstable.mono "$latest_mono"
do_update
'';
};
staging = fetchFromGitHub rec {
# https://github.com/wine-staging/wine-staging/releases
inherit (unstable) version;
hash = "sha256-lE/95OZigifreaRRCPkvA+Z0FqsBmm018jD6leSysXU=";
owner = "wine-staging";
repo = "wine-staging";
rev = "v${version}";
disabledPatchsets = [ ];
};
wayland = fetchFromGitLab {
# https://gitlab.collabora.com/alf/wine/-/tree/wayland
version = "8.2";
hash = "sha256-Eb2SFBIeQQ3cVZkUQcwNT5mcYe0ShFxBdMc3BlqkwTo=";
domain = "gitlab.collabora.com";
owner = "alf";
repo = "wine";
rev = "b2547ddf9e08cafce98cf7734d5c4ec926ef3536";
inherit (unstable) gecko32 gecko64;
inherit (unstable) mono;
updateScript = writeShellScript "update-wine-wayland" ''
${updateScriptPreamble}
wayland_rev=$(get_source_attr wayland.rev)
latest_wayland_rev=$(curl -s 'https://gitlab.collabora.com/api/v4/projects/2847/repository/branches/wayland' | jq -r .commit.id)
if [[ "$wayland_rev" != "$latest_wayland_rev" ]]; then
latest_wayland=$(curl -s 'https://gitlab.collabora.com/alf/wine/-/raw/wayland/VERSION' | cut -f3 -d' ')
wayland_url=$(get_source_attr wayland.url)
set_version_and_hash wayland "$latest_wayland" "$(nix-prefetch-url --unpack "''${wayland_url/$wayland_rev/$latest_wayland_rev}")"
set_source_attr wayland rev "\"$latest_wayland_rev\""
fi
do_update
'';
};
winetricks = fetchFromGitHub rec {
# https://github.com/Winetricks/winetricks/releases
version = "20240105";
hash = "sha256-YTEgb19aoM54KK8/IjrspoChzVnWAEItDlTxpfpS52w=";
owner = "Winetricks";
repo = "winetricks";
rev = version;
updateScript = writeShellScript "update-winetricks" ''
${updateScriptPreamble}
winetricks_repourl=$(get_source_attr winetricks.gitRepoUrl)
latest_winetricks=$(list-git-tags --url="$winetricks_repourl" | grep -E '^[0-9]{8}$' | sort --reverse --numeric-sort | head -n 1)
autobump winetricks "$latest_winetricks" 'nix-prefetch-url --unpack'
do_update
'';
};
}

View file

@ -167,17 +167,6 @@
"nixpkgs-wayland.cachix.org-1:3lwxaILxMRkVhehr5StQprHdEo4IrE8sRho9R9HOLYA="
];
};
nix.buildMachines = [
{
hostName = "darwin-build-box.nix-community.org";
protocol = "ssh-ng";
systems = [ "aarch64-darwin" "x86_64-darwin" ];
supportedFeatures = inputs.nix-community-infra.darwinConfigurations.darwin01.config.nix.settings.system-features;
sshKey = "/secrets/community-builder-key";
sshUser = "chayleaf";
publicHostKey = "c3NoLWVkMjU1MTkgQUFBQUMzTnphQzFsWkRJMU5URTVBQUFBSUZ6OEZYU1ZFZGY4RnZETWZib3hoQjVWalNlN3kyV2dTYTA5cTFMNHQwOTkgCg==";
}
];
services.udev.packages = [
pkgs.android-udev-rules
];