You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android中如何通过主屏应用控制副屏并发送按键事件?

主屏应用控制副屏发送导航/选择按键事件方案

核心思路

通过系统原生API定位副屏上的目标窗口,模拟导航(上/下/左/右)及选择(回车)按键事件并发送给该窗口。核心是匹配副屏的独立screen ID找到对应窗口,再调用平台专属API发送按键消息。


分平台实现方案

Windows 平台(Win32 API)

  1. 定位副屏窗口
    遍历系统所有顶层窗口,通过GetWindowRect获取窗口位置,结合MonitorFromWindow获取窗口所属显示器的句柄,再通过GetMonitorInfo拿到显示器标识,匹配目标screen ID后锁定窗口。
  2. 发送按键事件
    使用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)

  1. 定位副屏窗口
    用XQueryTree遍历窗口,通过XGetWindowAttributes获取窗口位置,结合XRRGetScreenInfo获取显示器信息,匹配目标screen ID找到窗口。
  2. 发送按键事件
    调用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)

  1. 定位副屏窗口
    使用CGWindowListCopyWindowInfo获取所有窗口信息,通过kCGWindowScreenNumber匹配目标screen ID,拿到窗口所属进程的序列号(PSN)。
  2. 发送按键事件
    创建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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 02:30:51