diff options
Diffstat (limited to 'src/nomute.c')
-rw-r--r-- | src/nomute.c | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/src/nomute.c b/src/nomute.c new file mode 100644 index 0000000..861062a --- /dev/null +++ b/src/nomute.c @@ -0,0 +1,108 @@ +/* + * Copyright © 2023 Willian Henrique <wsimanbrazil@yahoo.com.br> + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +// TODO(linux): this is currently only built on Windows, but a linux +// implementation would be also useful for some games e.g. L4D2 + +#include "con_.h" +#include "errmsg.h" +#include "feature.h" +#include "hook.h" +#include "intdefs.h" +#include "mem.h" +#include "os.h" +#include "sst.h" +#include "x86.h" +#include "x86util.h" + +// these have to come after Windows.h (via os.h) and in this order +#include <mmeapi.h> +#include <dsound.h> + +FEATURE("inactive window audio control") + +DEF_CVAR_UNREG(snd_mute_losefocus, + "Keep playing audio while tabbed out (SST reimplementation)", 1, + CON_ARCHIVE | CON_HIDDEN) + +static IDirectSoundVtbl *ds_vt = 0; +static typeof(ds_vt->CreateSoundBuffer) orig_CreateSoundBuffer; +static con_cmdcbv1 snd_restart_cb = 0; + +// early init (via VDF) happens before config is loaded, and audio is set up +// after that, so we don't want to run snd_restart the first time the cvar is +// set, unless we were loaded later with plugin_load, in which case audio is +// already up and running and we'll want to restart it every time +bool skiprestart; +static void losefocuscb(struct con_var *v) { + if (!skiprestart) snd_restart_cb(); + skiprestart = false; +} + +static long __stdcall hook_CreateSoundBuffer(IDirectSound *this, + const DSBUFFERDESC *desc, IDirectSoundBuffer **buff, IUnknown *unk) { + if (!con_getvari(snd_mute_losefocus)) { + ((DSBUFFERDESC *)desc)->dwFlags |= DSBCAPS_GLOBALFOCUS; + } + return orig_CreateSoundBuffer(this, desc, buff, unk); +} + +PREINIT { + if (con_findvar("snd_mute_losefocus")) return false; + con_reg(snd_mute_losefocus); + return true; +} + +INIT { + skiprestart = sst_earlyloaded; // see above + IDirectSound *ds_obj = 0; + if (DirectSoundCreate(NULL, &ds_obj, NULL) != DS_OK) { + errmsg_errorx("couldn't create IDirectSound instance"); + return false; + } + + ds_vt = ds_obj->lpVtbl; + if (!os_mprot(&ds_vt->CreateSoundBuffer, sizeof(void *), PAGE_READWRITE)) { + errmsg_errorx("couldn't make virtual table writable"); + return false; + } + + ds_obj->lpVtbl->Release(ds_obj); + + orig_CreateSoundBuffer = ds_vt->CreateSoundBuffer; + ds_vt->CreateSoundBuffer = &hook_CreateSoundBuffer; + snd_mute_losefocus->base.flags &= ~CON_HIDDEN; + + struct con_cmd *snd_restart = con_findcmd("snd_restart"); + if (snd_restart) { + snd_restart_cb = con_getcmdcbv1(snd_restart); + snd_mute_losefocus->cb = &losefocuscb; + } + else { + errmsg_warnx("couldn't find snd_restart"); + errmsg_note("changing snd_mute_losefocus will require changing other " + "audio settings or restarting the game with SST autoloaded in " + "order to have an effect"); + } + + return true; +} + +END { + ds_vt->CreateSoundBuffer = orig_CreateSoundBuffer; +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 |