mirror of
https://github.com/saphid/frame-control.git
synced 2026-10-06 06:00:33 +02:00
Refuse window input when Windows cannot focus its target
This commit is contained in:
1 parent
da5c954ce4
commit
6104c3f192
3 files changed
+30
-3
No files matched your search
@@ -41,7 +41,9 @@ API choices are **documented**, not device verification:
|
|||||||
|
|
||||||
Windows needs a WGC-capable Windows 10/11 desktop and an available hardware
|
Windows needs a WGC-capable Windows 10/11 desktop and an available hardware
|
||||||
Media Foundation H.264 encoder. Elevated windows and the secure desktop
|
Media Foundation H.264 encoder. Elevated windows and the secure desktop
|
||||||
cannot be driven by an ordinary Frame Control process. Minimized/closed
|
cannot be driven by an ordinary Frame Control process. If Windows refuses
|
||||||
|
to focus a selected window, input is refused rather than sent to the app
|
||||||
|
covering it; bring the selected window forward, then Stop and Show again. Minimized/closed
|
||||||
windows may stop producing frames. Protected content is not supported.
|
windows may stop producing frames. Protected content is not supported.
|
||||||
|
|
||||||
On Linux, press **Choose a window or screen…** and approve the desktop's
|
On Linux, press **Choose a window or screen…** and approve the desktop's
|
||||||
|
|||||||
@@ -93,6 +93,16 @@ class AdapterTests(unittest.TestCase):
|
|||||||
self.assertIn(mock.call(mouse=(0, 0, 0, 0x10)), host.send.call_args_list)
|
self.assertIn(mock.call(mouse=(0, 0, 0, 0x10)), host.send.call_args_list)
|
||||||
self.assertIn(mock.call(key=(162, 0, 2)), host.send.call_args_list)
|
self.assertIn(mock.call(key=(162, 0, 2)), host.send.call_args_list)
|
||||||
|
|
||||||
|
def test_refused_windows_focus_never_clicks_the_covering_app(self):
|
||||||
|
host = mock.Mock()
|
||||||
|
host.rect.return_value = (0, 0, 800, 600)
|
||||||
|
host.user.GetForegroundWindow.return_value = 99
|
||||||
|
host.user.SetForegroundWindow.return_value = False
|
||||||
|
inp = capture.WindowsInput(host, dict(src='window:12', w=800, h=600))
|
||||||
|
with self.assertRaisesRegex(RuntimeError, 'could not focus'):
|
||||||
|
inp.handle(dict(t='m', e='down', b=0, x=.5, y=.5))
|
||||||
|
host.send.assert_not_called()
|
||||||
|
|
||||||
def test_pc_reuses_launcher_and_namespaces_panel_ids(self):
|
def test_pc_reuses_launcher_and_namespaces_panel_ids(self):
|
||||||
view = frame_pcview.PCView(['ssh'], mock.Mock(return_value='panel created'), 'frame')
|
view = frame_pcview.PCView(['ssh'], mock.Mock(return_value='panel created'), 'frame')
|
||||||
view.call = mock.Mock(side_effect=lambda path, **kw: {'screen': True, 'ticket': 'one-use', 'streams': []})
|
view.call = mock.Mock(side_effect=lambda path, **kw: {'screen': True, 'ticket': 'one-use', 'streams': []})
|
||||||
|
|||||||
+17
-2
@@ -217,6 +217,8 @@ class Windows:
|
|||||||
self.user.GetWindowTextW.argtypes = [W.HWND, W.LPWSTR, C.c_int]
|
self.user.GetWindowTextW.argtypes = [W.HWND, W.LPWSTR, C.c_int]
|
||||||
self.user.GetWindowRect.argtypes = [W.HWND, C.POINTER(W.RECT)]
|
self.user.GetWindowRect.argtypes = [W.HWND, C.POINTER(W.RECT)]
|
||||||
self.user.SetForegroundWindow.argtypes = [W.HWND]
|
self.user.SetForegroundWindow.argtypes = [W.HWND]
|
||||||
|
self.user.GetForegroundWindow.argtypes = []
|
||||||
|
self.user.GetForegroundWindow.restype = W.HWND
|
||||||
self.dwm.DwmGetWindowAttribute.argtypes = [W.HWND, W.DWORD, C.c_void_p, W.DWORD]
|
self.dwm.DwmGetWindowAttribute.argtypes = [W.HWND, W.DWORD, C.c_void_p, W.DWORD]
|
||||||
self.callback = C.WINFUNCTYPE(W.BOOL, W.HWND, W.LPARAM)
|
self.callback = C.WINFUNCTYPE(W.BOOL, W.HWND, W.LPARAM)
|
||||||
self.monitor_callback = C.WINFUNCTYPE(W.BOOL, W.HMONITOR, W.HDC, C.POINTER(W.RECT), W.LPARAM)
|
self.monitor_callback = C.WINFUNCTYPE(W.BOOL, W.HMONITOR, W.HDC, C.POINTER(W.RECT), W.LPARAM)
|
||||||
@@ -305,6 +307,16 @@ class WindowsInput:
|
|||||||
if error:
|
if error:
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
|
def focus(self):
|
||||||
|
if not self.source['src'].startswith('window:'):
|
||||||
|
return
|
||||||
|
hwnd = int(self.source['src'].split(':')[1])
|
||||||
|
user = self.host.user
|
||||||
|
if user.GetForegroundWindow() != hwnd and not user.SetForegroundWindow(hwnd):
|
||||||
|
raise RuntimeError('Windows could not focus this window. Bring it to the front on your PC, then press Stop and Show again.')
|
||||||
|
if user.GetForegroundWindow() != hwnd:
|
||||||
|
raise RuntimeError('The selected window is not in front. Reopen the view after bringing it to the front on your PC.')
|
||||||
|
|
||||||
def handle(self, m):
|
def handle(self, m):
|
||||||
t, u = m['t'], self.host.user
|
t, u = m['t'], self.host.user
|
||||||
if t == 'release':
|
if t == 'release':
|
||||||
@@ -314,8 +326,8 @@ class WindowsInput:
|
|||||||
if source['src'].startswith('window:'):
|
if source['src'].startswith('window:'):
|
||||||
hwnd = int(source['src'].split(':')[1])
|
hwnd = int(source['src'].split(':')[1])
|
||||||
x, y, w, h = self.host.rect(hwnd)
|
x, y, w, h = self.host.rect(hwnd)
|
||||||
if m.get('e') == 'down':
|
if m.get('e') == 'down' or t == 'wheel':
|
||||||
u.SetForegroundWindow(hwnd)
|
self.focus()
|
||||||
else:
|
else:
|
||||||
x, y, w, h = (source[k] for k in ('x', 'y', 'w', 'h'))
|
x, y, w, h = (source[k] for k in ('x', 'y', 'w', 'h'))
|
||||||
x += max(0, min(1, float(m.get('x', 0)))) * (w - 1)
|
x += max(0, min(1, float(m.get('x', 0)))) * (w - 1)
|
||||||
@@ -335,6 +347,8 @@ class WindowsInput:
|
|||||||
self.host.send(mouse=(0, 0, delta & 0xffffffff, flag))
|
self.host.send(mouse=(0, 0, delta & 0xffffffff, flag))
|
||||||
elif t == 'k':
|
elif t == 'k':
|
||||||
code, down = str(m.get('code', '')), m.get('e') == 'down'
|
code, down = str(m.get('code', '')), m.get('e') == 'down'
|
||||||
|
if down:
|
||||||
|
self.focus()
|
||||||
special = {'Enter': 13, 'Escape': 27, 'Tab': 9, 'Backspace': 8, 'Space': 32,
|
special = {'Enter': 13, 'Escape': 27, 'Tab': 9, 'Backspace': 8, 'Space': 32,
|
||||||
'ArrowLeft': 37, 'ArrowUp': 38, 'ArrowRight': 39, 'ArrowDown': 40,
|
'ArrowLeft': 37, 'ArrowUp': 38, 'ArrowRight': 39, 'ArrowDown': 40,
|
||||||
'Delete': 46, 'Home': 36, 'End': 35, 'PageUp': 33, 'PageDown': 34,
|
'Delete': 46, 'Home': 36, 'End': 35, 'PageUp': 33, 'PageDown': 34,
|
||||||
@@ -350,6 +364,7 @@ class WindowsInput:
|
|||||||
elif down and len(str(m.get('key', ''))) == 1:
|
elif down and len(str(m.get('key', ''))) == 1:
|
||||||
self.handle({'t': 'text', 's': m['key']})
|
self.handle({'t': 'text', 's': m['key']})
|
||||||
elif t == 'text':
|
elif t == 'text':
|
||||||
|
self.focus()
|
||||||
raw = str(m.get('s', ''))[:4096].encode('utf-16-le')
|
raw = str(m.get('s', ''))[:4096].encode('utf-16-le')
|
||||||
for i in range(0, len(raw), 2):
|
for i in range(0, len(raw), 2):
|
||||||
unit = int.from_bytes(raw[i:i+2], 'little')
|
unit = int.from_bytes(raw[i:i+2], 'little')
|
||||||
|
|||||||
Reference in new issue
Block a user