Skip to content

Commit 56f9877

Browse files
committed
v5.20: Keyboard isolation - only owner's paired keyboard can type
- Fixed keyboard rejection logic to require positive identification - Request user list from server on connect (user.list.request.A2M) - Parse user.list.notify.M2A with devices[] array structure - Handle user.changed.notify.M2A for dynamic user updates - Block native keyboard input, only accept MouseMux events - Track per-window keyboard and mouse button state in InputFilter - KeyboardLayout uses InputFilter state instead of GetKeyState()
1 parent 0d6a533 commit 56f9877

File tree

5 files changed

+471
-65
lines changed

5 files changed

+471
-65
lines changed

widget/windows/InputFilter.cpp

Lines changed: 198 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,18 @@
88
namespace mozilla {
99
namespace widget {
1010

11+
// Static member definitions
1112
std::map<HWND, bool> InputFilter::sEnabledWindows;
1213
std::mutex InputFilter::sMutex;
1314
std::map<HWND, InputFilter::CursorPos> InputFilter::sCursorPositions;
1415
std::mutex InputFilter::sCursorMutex;
16+
std::map<HWND, InputFilter::KeyboardState> InputFilter::sKeyboardStates;
17+
std::mutex InputFilter::sKeyboardMutex;
18+
std::map<HWND, InputFilter::MouseButtonState> InputFilter::sMouseButtonStates;
19+
std::mutex InputFilter::sMouseButtonMutex;
1520

1621
HWND InputFilter::GetTopLevelWindow(HWND hwnd) {
1722
if (!hwnd) return nullptr;
18-
19-
// Walk up the parent chain to find the top-level window
2023
HWND parent = hwnd;
2124
HWND next;
2225
while ((next = ::GetParent(parent)) != nullptr) {
@@ -25,38 +28,32 @@ HWND InputFilter::GetTopLevelWindow(HWND hwnd) {
2528
return parent;
2629
}
2730

31+
// Window enable/disable
2832
void InputFilter::EnableForWindow(HWND hwnd) {
2933
HWND topLevel = GetTopLevelWindow(hwnd);
3034
if (!topLevel) topLevel = hwnd;
31-
3235
std::lock_guard<std::mutex> lock(sMutex);
3336
sEnabledWindows[topLevel] = true;
3437
}
3538

3639
void InputFilter::DisableForWindow(HWND hwnd) {
3740
HWND topLevel = GetTopLevelWindow(hwnd);
3841
if (!topLevel) topLevel = hwnd;
39-
4042
std::lock_guard<std::mutex> lock(sMutex);
4143
sEnabledWindows[topLevel] = false;
4244
}
4345

4446
bool InputFilter::IsEnabledForWindow(HWND hwnd) {
4547
HWND topLevel = GetTopLevelWindow(hwnd);
4648
if (!topLevel) topLevel = hwnd;
47-
4849
std::lock_guard<std::mutex> lock(sMutex);
4950
auto it = sEnabledWindows.find(topLevel);
50-
if (it != sEnabledWindows.end()) {
51-
return it->second;
52-
}
53-
return false; // Not in map means not enabled
51+
return (it != sEnabledWindows.end()) ? it->second : false;
5452
}
5553

5654
void InputFilter::RemoveWindow(HWND hwnd) {
5755
HWND topLevel = GetTopLevelWindow(hwnd);
5856
if (!topLevel) topLevel = hwnd;
59-
6057
{
6158
std::lock_guard<std::mutex> lock(sMutex);
6259
sEnabledWindows.erase(topLevel);
@@ -65,12 +62,98 @@ void InputFilter::RemoveWindow(HWND hwnd) {
6562
std::lock_guard<std::mutex> lock(sCursorMutex);
6663
sCursorPositions.erase(topLevel);
6764
}
65+
{
66+
std::lock_guard<std::mutex> lock(sKeyboardMutex);
67+
sKeyboardStates.erase(topLevel);
68+
}
69+
{
70+
std::lock_guard<std::mutex> lock(sMouseButtonMutex);
71+
sMouseButtonStates.erase(topLevel);
72+
}
73+
}
74+
75+
// Message type detection
76+
bool InputFilter::IsKeyboardMessage(UINT msg) {
77+
switch (msg) {
78+
case WM_KEYDOWN:
79+
case WM_KEYUP:
80+
case WM_SYSKEYDOWN:
81+
case WM_SYSKEYUP:
82+
case WM_CHAR:
83+
case WM_SYSCHAR:
84+
case WM_DEADCHAR:
85+
case WM_SYSDEADCHAR:
86+
case WM_UNICHAR:
87+
case WM_HOTKEY:
88+
case WM_IME_KEYDOWN:
89+
case WM_IME_KEYUP:
90+
case WM_IME_CHAR:
91+
case WM_IME_COMPOSITION:
92+
case WM_IME_STARTCOMPOSITION:
93+
case WM_IME_ENDCOMPOSITION:
94+
return true;
95+
default:
96+
return false;
97+
}
98+
}
99+
100+
bool InputFilter::IsMouseMessage(UINT msg) {
101+
switch (msg) {
102+
case WM_MOUSEMOVE:
103+
case WM_LBUTTONDOWN:
104+
case WM_LBUTTONUP:
105+
case WM_LBUTTONDBLCLK:
106+
case WM_RBUTTONDOWN:
107+
case WM_RBUTTONUP:
108+
case WM_RBUTTONDBLCLK:
109+
case WM_MBUTTONDOWN:
110+
case WM_MBUTTONUP:
111+
case WM_MBUTTONDBLCLK:
112+
case WM_XBUTTONDOWN:
113+
case WM_XBUTTONUP:
114+
case WM_XBUTTONDBLCLK:
115+
case WM_MOUSEWHEEL:
116+
case WM_MOUSEHWHEEL:
117+
case WM_NCMOUSEMOVE:
118+
case WM_NCLBUTTONDOWN:
119+
case WM_NCLBUTTONUP:
120+
case WM_NCLBUTTONDBLCLK:
121+
case WM_NCRBUTTONDOWN:
122+
case WM_NCRBUTTONUP:
123+
case WM_NCRBUTTONDBLCLK:
124+
case WM_NCMBUTTONDOWN:
125+
case WM_NCMBUTTONUP:
126+
case WM_NCMBUTTONDBLCLK:
127+
return true;
128+
default:
129+
return false;
130+
}
131+
}
132+
133+
bool InputFilter::IsInputMessage(UINT msg) {
134+
return IsKeyboardMessage(msg) || IsMouseMessage(msg);
135+
}
136+
137+
bool InputFilter::ShouldBlockNativeInput(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
138+
// If filtering not enabled for this window, don't block anything
139+
if (!IsEnabledForWindow(hwnd)) {
140+
return false;
141+
}
142+
143+
// Only block input messages (keyboard and mouse)
144+
if (!IsInputMessage(msg)) {
145+
return false;
146+
}
147+
148+
// Block all native input when filter is enabled
149+
// MouseMux will inject its own events which bypass this filter
150+
return true;
68151
}
69152

153+
// Cursor position tracking
70154
void InputFilter::SetCursorPosForWindow(HWND hwnd, int screenX, int screenY) {
71155
HWND topLevel = GetTopLevelWindow(hwnd);
72156
if (!topLevel) topLevel = hwnd;
73-
74157
std::lock_guard<std::mutex> lock(sCursorMutex);
75158
CursorPos& pos = sCursorPositions[topLevel];
76159
pos.screenX = screenX;
@@ -81,15 +164,117 @@ void InputFilter::SetCursorPosForWindow(HWND hwnd, int screenX, int screenY) {
81164
bool InputFilter::GetCursorPosForWindow(HWND hwnd, POINT* outPos) {
82165
HWND topLevel = GetTopLevelWindow(hwnd);
83166
if (!topLevel) topLevel = hwnd;
84-
85167
std::lock_guard<std::mutex> lock(sCursorMutex);
86168
auto it = sCursorPositions.find(topLevel);
87169
if (it != sCursorPositions.end() && it->second.valid) {
88170
outPos->x = it->second.screenX;
89171
outPos->y = it->second.screenY;
90172
return true;
91173
}
92-
return false; // No valid position stored
174+
return false;
175+
}
176+
177+
// Keyboard state management
178+
void InputFilter::SetKeyStateForWindow(HWND hwnd, BYTE* keyState) {
179+
HWND topLevel = GetTopLevelWindow(hwnd);
180+
if (!topLevel) topLevel = hwnd;
181+
std::lock_guard<std::mutex> lock(sKeyboardMutex);
182+
KeyboardState& state = sKeyboardStates[topLevel];
183+
memcpy(state.keys, keyState, 256);
184+
state.valid = true;
185+
}
186+
187+
bool InputFilter::GetKeyStateForWindow(HWND hwnd, BYTE* outKeyState) {
188+
HWND topLevel = GetTopLevelWindow(hwnd);
189+
if (!topLevel) topLevel = hwnd;
190+
std::lock_guard<std::mutex> lock(sKeyboardMutex);
191+
auto it = sKeyboardStates.find(topLevel);
192+
if (it != sKeyboardStates.end() && it->second.valid) {
193+
memcpy(outKeyState, it->second.keys, 256);
194+
return true;
195+
}
196+
return false;
197+
}
198+
199+
void InputFilter::SetSingleKeyState(HWND hwnd, int vkey, bool down, bool toggled) {
200+
if (vkey < 0 || vkey > 255) return;
201+
202+
HWND topLevel = GetTopLevelWindow(hwnd);
203+
if (!topLevel) topLevel = hwnd;
204+
std::lock_guard<std::mutex> lock(sKeyboardMutex);
205+
KeyboardState& state = sKeyboardStates[topLevel];
206+
207+
// High bit (0x80) = key is down
208+
// Low bit (0x01) = key is toggled (for toggle keys like CapsLock)
209+
BYTE val = 0;
210+
if (down) val |= 0x80;
211+
if (toggled) val |= 0x01;
212+
state.keys[vkey] = val;
213+
state.valid = true;
214+
}
215+
216+
// Mouse button state management
217+
void InputFilter::SetMouseButtonState(HWND hwnd, bool left, bool right, bool middle) {
218+
HWND topLevel = GetTopLevelWindow(hwnd);
219+
if (!topLevel) topLevel = hwnd;
220+
std::lock_guard<std::mutex> lock(sMouseButtonMutex);
221+
MouseButtonState& state = sMouseButtonStates[topLevel];
222+
state.left = left;
223+
state.right = right;
224+
state.middle = middle;
225+
}
226+
227+
WORD InputFilter::GetMouseButtonState(HWND hwnd) {
228+
HWND topLevel = GetTopLevelWindow(hwnd);
229+
if (!topLevel) topLevel = hwnd;
230+
std::lock_guard<std::mutex> lock(sMouseButtonMutex);
231+
auto it = sMouseButtonStates.find(topLevel);
232+
if (it == sMouseButtonStates.end()) {
233+
return 0;
234+
}
235+
WORD flags = 0;
236+
if (it->second.left) flags |= MK_LBUTTON;
237+
if (it->second.right) flags |= MK_RBUTTON;
238+
if (it->second.middle) flags |= MK_MBUTTON;
239+
return flags;
240+
}
241+
242+
// Thread-local current window for KeyboardLayout to query
243+
static thread_local HWND sCurrentProcessingWindow = nullptr;
244+
245+
void InputFilter::SetCurrentWindow(HWND hwnd) {
246+
sCurrentProcessingWindow = hwnd;
247+
}
248+
249+
HWND InputFilter::GetCurrentWindow() {
250+
return sCurrentProcessingWindow;
251+
}
252+
253+
void InputFilter::ClearCurrentWindow() {
254+
sCurrentProcessingWindow = nullptr;
255+
}
256+
257+
bool InputFilter::GetCurrentMouseButtons(uint16_t* outButtons) {
258+
HWND hwnd = sCurrentProcessingWindow;
259+
if (!hwnd || !outButtons) {
260+
return false;
261+
}
262+
263+
if (!IsEnabledForWindow(hwnd)) {
264+
return false; // Use native GetKeyState
265+
}
266+
267+
// Get button state from our tracked state
268+
WORD flags = GetMouseButtonState(hwnd);
269+
*outButtons = 0;
270+
271+
// Convert MK_* flags to MouseButtonsFlag values
272+
// MouseButtonsFlag::ePrimaryFlag = 1, eSecondaryFlag = 2, eMiddleFlag = 4
273+
if (flags & MK_LBUTTON) *outButtons |= 1; // ePrimaryFlag
274+
if (flags & MK_RBUTTON) *outButtons |= 2; // eSecondaryFlag
275+
if (flags & MK_MBUTTON) *outButtons |= 4; // eMiddleFlag
276+
277+
return true;
93278
}
94279

95280
} // namespace widget

widget/windows/InputFilter.h

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,52 @@
1313
namespace mozilla {
1414
namespace widget {
1515

16-
// Per-window flag to block native mouse input in Firefox
17-
// When enabled for a window, nsWindow skips processing native mouse messages
18-
// for that specific window only. Each window is independent.
16+
// Per-window input filtering for MouseMux multi-user support.
17+
// When enabled for a window:
18+
// - Native Windows mouse/keyboard input is blocked
19+
// - Only MouseMux-injected input is processed
20+
// - Each window tracks its own cursor position and keyboard state
1921
class InputFilter {
2022
public:
23+
// Enable/disable native input blocking per window
2124
static void EnableForWindow(HWND hwnd);
2225
static void DisableForWindow(HWND hwnd);
2326
static bool IsEnabledForWindow(HWND hwnd);
24-
static void RemoveWindow(HWND hwnd); // Cleanup when window is destroyed
27+
static void RemoveWindow(HWND hwnd);
2528

26-
// Per-window cursor position tracking for MouseMux
27-
// Stores the last known MouseMux cursor position per-window
29+
// Message type detection - used to identify what to block
30+
static bool IsKeyboardMessage(UINT msg);
31+
static bool IsMouseMessage(UINT msg);
32+
static bool IsInputMessage(UINT msg);
33+
34+
// Check if a native input message should be blocked
35+
// Returns true if the message should NOT be processed (blocked)
36+
static bool ShouldBlockNativeInput(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
37+
38+
// Per-window cursor position tracking (MouseMux cursor, not system cursor)
2839
static void SetCursorPosForWindow(HWND hwnd, int screenX, int screenY);
2940
static bool GetCursorPosForWindow(HWND hwnd, POINT* outPos);
3041

42+
// Per-window keyboard state from MouseMux
43+
// This replaces native GetKeyboardState() calls when filtering is enabled
44+
static void SetKeyStateForWindow(HWND hwnd, BYTE* keyState);
45+
static bool GetKeyStateForWindow(HWND hwnd, BYTE* outKeyState);
46+
static void SetSingleKeyState(HWND hwnd, int vkey, bool down, bool toggled = false);
47+
48+
// Per-window mouse button state from MouseMux
49+
static void SetMouseButtonState(HWND hwnd, bool left, bool right, bool middle);
50+
static WORD GetMouseButtonState(HWND hwnd);
51+
52+
// Current window being processed (for use by KeyboardLayout)
53+
// Set this before calling ModifierKeyState functions
54+
static void SetCurrentWindow(HWND hwnd);
55+
static HWND GetCurrentWindow();
56+
static void ClearCurrentWindow();
57+
58+
// Get mouse button state for current window (called by KeyboardLayout)
59+
// Returns flags compatible with MouseButtonsFlag enum
60+
static bool GetCurrentMouseButtons(uint16_t* outButtons);
61+
3162
private:
3263
static std::map<HWND, bool> sEnabledWindows;
3364
static std::mutex sMutex;
@@ -41,7 +72,23 @@ class InputFilter {
4172
static std::map<HWND, CursorPos> sCursorPositions;
4273
static std::mutex sCursorMutex;
4374

44-
// Helper to find the top-level window for a child window
75+
// Per-window keyboard state (256 bytes, same as Windows keyboard state)
76+
struct KeyboardState {
77+
BYTE keys[256] = {0};
78+
bool valid = false;
79+
};
80+
static std::map<HWND, KeyboardState> sKeyboardStates;
81+
static std::mutex sKeyboardMutex;
82+
83+
// Per-window mouse button state
84+
struct MouseButtonState {
85+
bool left = false;
86+
bool right = false;
87+
bool middle = false;
88+
};
89+
static std::map<HWND, MouseButtonState> sMouseButtonStates;
90+
static std::mutex sMouseButtonMutex;
91+
4592
static HWND GetTopLevelWindow(HWND hwnd);
4693
};
4794

widget/windows/KeyboardLayout.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828

2929
#include "WidgetUtils.h"
3030
#include "WinUtils.h"
31+
#include "InputFilter.h"
3132

3233
#include "npapi.h"
3334

@@ -910,6 +911,16 @@ void ModifierKeyState::InitMouseEvent(WidgetInputEvent& aMouseEvent) const {
910911

911912
WidgetMouseEventBase& mouseEvent = *aMouseEvent.AsMouseEventBase();
912913
mouseEvent.mButtons = 0;
914+
915+
// Try to get button state from InputFilter (MouseMux per-window state)
916+
uint16_t mouseMuxButtons = 0;
917+
if (InputFilter::GetCurrentMouseButtons(&mouseMuxButtons)) {
918+
// Use MouseMux tracked button state instead of native GetKeyState
919+
mouseEvent.mButtons = static_cast<int16_t>(mouseMuxButtons);
920+
return;
921+
}
922+
923+
// Fall back to native GetKeyState for non-MouseMux windows
913924
if (::GetKeyState(VK_LBUTTON) < 0) {
914925
mouseEvent.mButtons |= MouseButtonsFlag::ePrimaryFlag;
915926
}

0 commit comments

Comments
 (0)