Android中如何通过主屏应用控制副屏并发送按键事件?
主屏应用控制副屏发送导航/选择按键事件方案
核心思路
通过系统原生API定位副屏上的目标窗口,模拟导航(上/下/左/右)及选择(回车)按键事件并发送给该窗口。核心是匹配副屏的独立screen ID找到对应窗口,再调用平台专属API发送按键消息。
分平台实现方案
Windows 平台(Win32 API)
- 定位副屏窗口
遍历系统所有顶层窗口,通过GetWindowRect获取窗口位置,结合MonitorFromWindow获取窗口所属显示器的句柄,再通过GetMonitorInfo拿到显示器标识,匹配目标screen ID后锁定窗口。 - 发送按键事件
使用PostMessage向目标窗口发送WM_KEYDOWN/WM_KEYUP消息,或用SendInput模拟硬件级按键输入。
示例代码(C#):
using System; using System.Runtime.InteropServices; using System.Windows.Forms; public class SecondaryScreenKeySender { [DllImport("user32.dll")] private static extern bool EnumWindows(EnumWindowsProc enumProc, IntPtr lParam); [DllImport("user32.dll")] private static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags); [DllImport("user32.dll")] private static extern bool GetMonitorInfo(IntPtr hMonitor, ref MONITORINFO lpmi); [DllImport("user32.dll")] private static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); private struct MONITORINFO { public uint cbSize; public RECT rcMonitor; public RECT rcWork; public uint dwFlags; } private struct RECT { public int Left, Top, Right, Bottom; } private const uint WM_KEYDOWN = 0x0100; private const uint WM_KEYUP = 0x0101; // 匹配screen ID找到副屏窗口句柄 public static IntPtr FindSecondaryScreenWindow(int targetScreenId) { IntPtr targetHwnd = IntPtr.Zero; EnumWindows((hWnd, lParam) => { var monitor = MonitorFromWindow(hWnd, 2); var monitorInfo = new MONITORINFO { cbSize = (uint)Marshal.SizeOf(typeof(MONITORINFO)) }; if (GetMonitorInfo(monitor, ref monitorInfo)) { foreach (Screen screen in Screen.AllScreens) { // 此处根据你的screen ID关联逻辑调整,示例通过设备名匹配 if (screen.DeviceName.Contains(monitorInfo.rcMonitor.ToString()) && screen.GetHashCode() == targetScreenId) { targetHwnd = hWnd; return false; } } } return true; }, IntPtr.Zero); return targetHwnd; } // 发送导航按键(示例:向下键) public static void SendNavigationKey(IntPtr hWnd, Keys key) { PostMessage(hWnd, WM_KEYDOWN, (IntPtr)key, IntPtr.Zero); PostMessage(hWnd, WM_KEYUP, (IntPtr)key, IntPtr.Zero); } }
Linux 平台(X11 API)
- 定位副屏窗口
用XQueryTree遍历窗口,通过XGetWindowAttributes获取窗口位置,结合XRRGetScreenInfo获取显示器信息,匹配目标screen ID找到窗口。 - 发送按键事件
调用XSendEvent发送KeyPress/KeyRelease事件到目标窗口。
示例代码片段(C):
#include <X11/Xlib.h> #include <X11/extensions/Xrandr.h> // 找到副屏窗口 Window find_secondary_screen_window(Display *dpy, int target_screen_id) { Window root = RootWindow(dpy, DefaultScreen(dpy)); Window parent, *children; unsigned int nchildren; XQueryTree(dpy, root, &root, &parent, &children, &nchildren); for (unsigned int i = 0; i < nchildren; i++) { XWindowAttributes attrs; XGetWindowAttributes(dpy, children[i], &attrs); XRRScreenInfo *info = XRRGetScreenInfo(dpy, root); int screen_num = XRRGetOutputPrimary(dpy, info); if (screen_num == target_screen_id) { XFree(children); XRRFreeScreenInfo(info); return children[i]; } XRRFreeScreenInfo(info); } XFree(children); return None; } // 发送回车选择按键 void send_select_key(Display *dpy, Window win) { XEvent event; memset(&event, 0, sizeof(event)); event.type = KeyPress; event.xkey.window = win; event.xkey.keycode = XKeysymToKeycode(dpy, XK_Return); XSendEvent(dpy, win, True, KeyPressMask, &event); event.type = KeyRelease; XSendEvent(dpy, win, True, KeyReleaseMask, &event); XFlush(dpy); }
macOS 平台(Quartz Event Services)
- 定位副屏窗口
使用CGWindowListCopyWindowInfo获取所有窗口信息,通过kCGWindowScreenNumber匹配目标screen ID,拿到窗口所属进程的序列号(PSN)。 - 发送按键事件
创建CGEventRef键盘事件,通过CGEventPostToPSN发送到目标进程。
示例代码(Objective-C):
#import <Cocoa/Cocoa.h> #import <QuartzCore/QuartzCore.h> // 获取副屏窗口的进程序列号 static BOOL getSecondaryScreenWindowPSN(int targetScreenId, ProcessSerialNumber *psn) { CFArrayRef windowList = CGWindowListCopyWindowInfo(kCGWindowListOptionAll, kCGNullWindowID); for (NSDictionary *windowInfo in (__bridge NSArray *)windowList) { NSNumber *screenNum = windowInfo[(id)kCGWindowScreenNumber]; if ([screenNum intValue] == targetScreenId) { NSNumber *pid = windowInfo[(id)kCGWindowOwnerPID]; return GetProcessForPID([pid intValue], psn) == noErr; } } CFRelease(windowList); return NO; } // 发送向右导航按键 void sendNavigationRightKey(ProcessSerialNumber *psn) { CGEventRef keyDown = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)124, true); CGEventRef keyUp = CGEventCreateKeyboardEvent(NULL, (CGKeyCode)124, false); CGEventPostToPSN(psn, keyDown); CGEventPostToPSN(psn, keyUp); CFRelease(keyDown); CFRelease(keyUp); }
关键注意事项
- 权限问题:macOS需开启「辅助功能」权限;Windows/Linux可能需要进程拥有跨窗口事件发送权限。
- 窗口激活状态:部分应用仅在前台激活时接收按键,可先调用
SetForegroundWindow(Windows)或CGWindowListActivateWindow(macOS)激活目标窗口。 - screen ID匹配逻辑:不同平台获取screen ID的方式有差异,需根据你的系统环境调整窗口与screen ID的关联判断逻辑。
内容的提问来源于stack exchange,提问作者suspended_null
相关产品推荐
相关产品推荐

