Skip to content
This repository was archived by the owner on May 5, 2025. It is now read-only.

Commit 07778f9

Browse files
committed
[REMIX-1578] Fix window message handling and DirectInput forwarding via new policy option
- Track both the device focus window and presentation window handles - Default the Remix UI activation scheme to presentation window - Add new DirectInput device forwarding policies and options - `client.DirectInput.forward.mousePolicy` - `client.DirectInput.forward.keyboardPolicy` - See `bridge.conf` for details - Deprecate old DirectInput device forwarding option - `client.DirectInput.forwardMessages`
2 parents 3dd0836 + 56f6d8d commit 07778f9

File tree

9 files changed

+186
-147
lines changed

9 files changed

+186
-147
lines changed

bridge.conf

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,6 @@
5050
# client.overrideCustomWinHooks = False
5151

5252

53-
# Forces DirectInput message forwarding to the Remix Runtime UI.
54-
# Typically this forwarding is only done with the Remix UI is
55-
# visible.
56-
#
57-
# Supported values: True, False
58-
59-
# client.DirectInput.forwardMessages = False
60-
61-
6253
# Disables DirectInput exclusive input. Only affects fullscreen modes.
6354
# When Remix UI input misbehaves in fullscreen mode, try disabling
6455
# DirectInput exclusive input.
@@ -68,6 +59,37 @@
6859
# client.DirectInput.disableExclusiveInput = False
6960

7061

62+
# Forwarding of DirectInput state to the standard windows message pump.
63+
# Some games disable some subset (or all) of keyboard/mouse input typically
64+
# handled by windows messages, in favor of using DirectInput as the handler.
65+
# This can fundamentally break how Remix and the bridge accept user input
66+
# for interacting with the Remix UI. To work around this, we translate DI
67+
# input into standard windows messages. Most games only need this when the
68+
# Remix UI is already up, which is why that is the default mode.
69+
#
70+
# All games are different, so when opening/closing/interacting with the
71+
# Remix UI, take note of what sort of input is (not) happening, and try
72+
# setting the policy accordingly.
73+
#
74+
# Common cases:
75+
# * If you notice that you are unable to bring up the Remix UI: Try setting
76+
# the keyboard policy to 3 (Always), so that we can properly send the
77+
# hotkey to active the UI. The game is likely swallowing all key input
78+
# otherwise.
79+
# * If you notice that when the Remix UI is open, there are two mice, and
80+
# sometimes one is even flickering: Try setting the mouse policy to 0
81+
# (Never) or 1 (Remix UI Inactive). The game is likely confused by our
82+
# bringing the standard system mouse back into play when this happens.
83+
#
84+
# Supported values: 0 (Never) - Never forward.
85+
# 1 (Remix UI Inactive) - Forward DI input only when Remix UI is inactive.
86+
# 2 (Remix UI Active) - Forward DI input only when Remix UI is active.
87+
# 3 (Always) - DI input is always forwarded.
88+
89+
# client.DirectInput.forward.mousePolicy = False
90+
# client.DirectInput.forward.keyboardPolicy = False
91+
92+
7193
# Forces windowed mode even for games that try to launch in fullscreen,
7294
# which is useful for troubleshooting and debugging purposes. Turned
7395
# off by default.

src/client/client_options.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
#include "config/config.h"
2828
#include "log/log.h"
29+
#include "di_hook.h"
2930

3031
#include <d3d9.h>
3132

@@ -46,14 +47,18 @@ namespace ClientOptions {
4647
return bridge_util::Config::getOption<bool>("client.overrideCustomWinHooks", false);
4748
}
4849

49-
inline bool getForwardDirectInputMessages() {
50-
return bridge_util::Config::getOption<bool>("client.DirectInput.forwardMessages", false);
51-
}
52-
5350
inline bool getDisableExclusiveInput() {
5451
return bridge_util::Config::getOption<bool>("client.DirectInput.disableExclusiveInput", false);
5552
}
5653

54+
inline DI::ForwardPolicy getForwardDirectInputMousePolicy() {
55+
return (DI::ForwardPolicy)bridge_util::Config::getOption<int>("client.DirectInput.forward.mousePolicy", DI::RemixUIActive);
56+
}
57+
58+
inline DI::ForwardPolicy getForwardDirectInputKeyboardPolicy() {
59+
return (DI::ForwardPolicy)bridge_util::Config::getOption<int>("client.DirectInput.forward.keyboardPolicy", DI::RemixUIActive);
60+
}
61+
5762
inline bool getForceWindowed() {
5863
return bridge_util::Config::getOption<bool>("client.forceWindowed", false);
5964
}

src/client/d3d9_device.cpp

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -420,7 +420,7 @@ HRESULT Direct3DDevice9Ex_LSS<EnableSync>::Reset(D3DPRESENT_PARAMETERS* pPresent
420420
ResetState();
421421
const auto presParam = Direct3DSwapChain9_LSS::sanitizePresentationParameters(*pPresentationParameters, getCreateParams());
422422
// Add a hook into this window if we don't already have it.
423-
setWinProc(presParam.hDeviceWindow);
423+
setWinProc(getWinProcHwnd());
424424
// Tell Server to do the Reset
425425
size_t currentUID = 0;
426426
{
@@ -494,16 +494,8 @@ HRESULT Direct3DDevice9Ex_LSS<EnableSync>::Present(CONST RECT* pSourceRect, CONS
494494
}
495495

496496
// Seeing this in the log could indicate the game is sending inputs to a different window
497-
extern std::unordered_map<HWND, std::deque<WNDPROC>> ogWndProcList;
498-
extern std::mutex gWndProcListMapMutex;
499-
if (hDestWindowOverride != NULL) {
500-
std::scoped_lock lock(gWndProcListMapMutex);
501-
if(ogWndProcList.find(hDestWindowOverride) == ogWndProcList.end())
502-
ONCE(Logger::info("Detected unhooked winproc on Direct3DDevice9::Present"));
503-
}
504-
const auto hWnd = (hDestWindowOverride != NULL) ? hDestWindowOverride : m_hWnd;
505-
if(GetWindowLongPtr(hWnd,GWLP_WNDPROC) != reinterpret_cast<LONG_PTR>(RemixWndProc)) {
506-
setWinProc(hWnd, true);
497+
if(GetWindowLongPtr(getWinProcHwnd(),GWLP_WNDPROC) != reinterpret_cast<LONG_PTR>(RemixWndProc)) {
498+
setWinProc(getWinProcHwnd(), true);
507499
Logger::warn("Detected non-Remix winproc on Present(), which is indicative of game resetting winproc after device creation. Remix winproc has been re-hooked. If this warning persists and remix GUI is not interactable, further intervention may be necessary.");
508500
}
509501

src/client/d3d9_device_base.cpp

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ BaseDirect3DDevice9Ex_LSS::BaseDirect3DDevice9Ex_LSS(const bool bExtended,
3636
: D3DBase<IDirect3DDevice9Ex>(nullptr, pDirect3D)
3737
, m_ex(bExtended)
3838
, m_pDirect3D(pDirect3D)
39-
, m_createParams(createParams) {
39+
, m_createParams(createParams)
40+
, m_presParams(presParams) {
4041
Logger::debug("Creating Device...");
4142

4243
// D3D9 seems to inialize its state to this
@@ -53,19 +54,19 @@ BaseDirect3DDevice9Ex_LSS::BaseDirect3DDevice9Ex_LSS(const bool bExtended,
5354

5455
// Initialize the implicit viewport
5556
memset(&m_state.viewport, 0, sizeof(m_state.viewport));
56-
m_state.viewport.Width = presParams.BackBufferWidth;
57-
m_state.viewport.Height = presParams.BackBufferHeight;
57+
m_state.viewport.Width = m_presParams.BackBufferWidth;
58+
m_state.viewport.Height = m_presParams.BackBufferHeight;
5859
m_state.viewport.MaxZ = 1.f;
5960

6061
// Games may override client's exception handler when it was setup early.
6162
// Attempt to restore the exeption handler.
6263
SetupExceptionHandler();
6364

64-
// MSDN: For windowed mode, this parameter may be NULL only if the hDeviceWindow member
65-
// of pPresentationParameters is set to a valid, non-NULL value.
65+
assert(m_createParams.hFocusWindow || m_presParams.hDeviceWindow);
66+
67+
setWinProc(getWinProcHwnd());
68+
Logger::info(format_string("WinProcWnd: %x", getWinProcHwnd()));
6669

67-
m_hWnd = createParams.hFocusWindow ? createParams.hFocusWindow : presParams.hDeviceWindow;
68-
setWinProc(m_hWnd);
6970
m_previousPresentParams = presParams;
7071
DWORD customBehaviorFlags = createParams.BehaviorFlags | D3DCREATE_NOWINDOWCHANGES;
7172
InitRamp();
@@ -84,14 +85,14 @@ BaseDirect3DDevice9Ex_LSS::BaseDirect3DDevice9Ex_LSS(const bool bExtended,
8485
}
8586
c.send_data(sizeof(D3DDISPLAYMODEEX), pFullscreenDisplayMode);
8687
}
87-
c.send_data(sizeof(D3DPRESENT_PARAMETERS), &presParams);
88+
c.send_data(sizeof(D3DPRESENT_PARAMETERS), &m_presParams);
8889
}
8990
Logger::debug("...server-side D3D9 device creation command sent...");
9091

9192
Logger::debug("...waiting for create device ack response from server...");
9293
if (Result::Success != ModuleBridge::waitForCommand(Commands::Bridge_Response, 0, nullptr, true, currentUID)) {
9394
Logger::err("...server-side D3D9 device creation failed with: no response from server.");
94-
removeWinProc(m_hWnd);
95+
removeWinProc(getWinProcHwnd());
9596
hresultOut = D3DERR_DEVICELOST;
9697
return;
9798
}
@@ -105,7 +106,7 @@ BaseDirect3DDevice9Ex_LSS::BaseDirect3DDevice9Ex_LSS(const bool bExtended,
105106
if (FAILED(hresultOut)) {
106107
Logger::err(format_string("...server-side D3D9 device creation failed with %x.", hresultOut));
107108
// Release client device and report server error to the app
108-
removeWinProc(m_hWnd);
109+
removeWinProc(getWinProcHwnd());
109110
return;
110111
}
111112
Logger::debug("...server-side D3D9 device successfully created...");

src/client/d3d9_device_base.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,13 @@ class BaseDirect3DDevice9Ex_LSS: public D3DBase<IDirect3DDevice9Ex>
140140
const D3DPRESENT_PARAMETERS& presParams,
141141
const D3DDISPLAYMODEEX* const pFullscreenDisplayMode,
142142
HRESULT& hresultOut);
143+
144+
HWND getFocusHwnd() const { return m_createParams.hFocusWindow; }
145+
HWND getPresentationHwnd() const { return m_presParams.hDeviceWindow; }
146+
HWND getWinProcHwnd() const { return getPresentationHwnd() ? getPresentationHwnd() : getFocusHwnd(); }
147+
143148
void InitRamp();
149+
144150
using ShaderType = ShaderConstants::ShaderType;
145151
using ConstantType = ShaderConstants::ConstantType;
146152

@@ -173,6 +179,7 @@ class BaseDirect3DDevice9Ex_LSS: public D3DBase<IDirect3DDevice9Ex>
173179
const bool m_ex;
174180
Direct3D9Ex_LSS* const m_pDirect3D = nullptr;
175181
const D3DDEVICE_CREATION_PARAMETERS m_createParams;
182+
const D3DPRESENT_PARAMETERS m_presParams;
176183

177184
D3DGAMMARAMP m_gammaRamp;
178185
D3DPRESENT_PARAMETERS m_previousPresentParams;
@@ -184,7 +191,6 @@ class BaseDirect3DDevice9Ex_LSS: public D3DBase<IDirect3DDevice9Ex>
184191
DWORD m_FVF;
185192
INT m_gpuThreadPriority;
186193
UINT m_maxFrameLatency;
187-
HWND m_hWnd = NULL;
188194

189195
struct StateCaptureDirtyFlags {
190196
// Vertex Decl

0 commit comments

Comments
 (0)