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

X11环境下Electron应用程序化拖拽启动异常问题求助

在X11环境下重实现electron-click-drag-plugin的EWMH事件异常问题

我正在为自身Electron应用重实现electron-click-drag-plugin,但在X11环境下遭遇瓶颈。已成功发送EWMH事件,但效果不符合预期,具体症状:

  • 发送事件后,窗口管理器进入异常状态,初期无明显异常,当光标移至目标窗口边缘时,窗口会随之移动,仿佛触发一次拖拽操作
  • 进入该状态后难以退出,退出方式可能因窗口管理器而异

实现代码

x11rb版本

// x11rb

let Ok((conn, _)) = x11rb::connect(None) else {
    let err_msg = cx.string("Failed to connect to X11 server");
    return cx.throw(err_msg);
};

let buf = handle.as_slice(cx);
if buf.len() != 4 {
    let err_msg = cx.string("Invalid buffer size for window handle");
    return cx.throw(err_msg);
}
let Some(window) = buf
    .get(0..4)
    .map(|b| u32::from_le_bytes(b.try_into().unwrap()))
else {
    let err_msg = cx.string("Failed to parse window handle");
    return cx.throw(err_msg);
};

let Ok(query_pointer_reply) = query_pointer(&conn, window)
    .map_err(|_| ())
    .and_then(|x| x.reply().map_err(|_| ()))
else {
    let err_msg = cx.string("Failed to query pointer position");
    return cx.throw(err_msg);
};

let Ok(move_atom_reply) = intern_atom(&conn, false, "_NET_WM_MOVERESIZE".as_bytes())
    .map_err(|_| ())
    .and_then(|x| x.reply().map_err(|_| ()))
else {
    let err_msg = cx.string("Failed to intern atom");
    return cx.throw(err_msg);
};

let roots = &conn.setup().roots;
if roots.is_empty() {
    let err_msg = cx.string("No screens found");
    return cx.throw(err_msg);
}

let root_window = roots[0].root;

let event = ClientMessageEvent::new(
    32,
    window,
    move_atom_reply.atom,
    ClientMessageData::from([
        query_pointer_reply.root_x as u32,
        query_pointer_reply.root_y as u32,
        8, // _NET_WM_MOVERESIZE_MOVE
        1, // Button 1 (left mouse button)
        0,
    ]),
);

if let Err(err) = send_event(
    &conn,
    false,
    root_window,
    EventMask::SUBSTRUCTURE_REDIRECT | EventMask::SUBSTRUCTURE_NOTIFY,
    event,
) {
    let err_msg = cx.string(format!("Failed to send client message: {}", err));
    return cx.throw(err_msg);
}

if let Err(err) = conn.flush() {
    let err_msg = cx.string(format!("Failed to flush X11 connection: {}", err));
    return cx.throw(err_msg);
}

Xlib动态绑定版本

// Dynamic resolves Xlib

let display = x_open_display(std::ptr::null());
if display.is_null() {
    let err_msg = cx.string("Failed to open X display");
    return cx.throw(err_msg);
}

let mut root_return: c_ulong = 0;
let mut child_return: c_ulong = 0;
let mut root_x: i32 = 0;
let mut root_y: i32 = 0;
let mut win_x: i32 = 0;
let mut win_y: i32 = 0;
let mut mask_return: u32 = 0;

let query_res = x_query_pointer(
    display,
    window,
    &mut root_return,
    &mut child_return,
    &mut root_x,
    &mut root_y,
    &mut win_x,
    &mut win_y,
    &mut mask_return,
);

if query_res == 0 {
    x_close_display(display);
    let err_msg = cx.string("XQueryPointer failed to query the pointer on the window");
    return cx.throw(err_msg);
}

let move_atom = x_intern_atom(display, c"_NET_WM_MOVERESIZE".as_ptr(), 0);
if move_atom == 0 {
    x_close_display(display);
    let err_msg = cx.string("XInternAtom failed to intern the _NET_WM_MOVERESIZE atom");
    return cx.throw(err_msg);
}

let root_window = x_default_root_window(display);
if root_window == 0 {
    x_close_display(display);
    let err_msg = cx.string("XDefaultRootWindow failed to get the default root window");
    return cx.throw(err_msg);
}

let mut event = std::mem::zeroed::<XEvent>();
let xclient = &mut event.xclient;
xclient.type_ = CLIENT_MESSAGE;
xclient.window = window;
xclient.message_type = move_atom;
xclient.format = 32;
xclient.data.l[0] = root_x as c_long;
xclient.data.l[1] = root_y as c_long;
xclient.data.l[2] = 8; // _NET_WM_MOVERESIZE_MOVE
xclient.data.l[3] = 1;
xclient.data.l[4] = 0;

let send_res = x_send_event(
    display,
    root_window,
    0,
    (SUBSTRUCTURE_REDIRECT_MASK | SUBSTRUCTURE_NOTIFY_MASK) as c_long,
    &mut event as *mut XEvent as *mut c_void,
);

if send_res == 0 {
    x_close_display(display);
    let err_msg = cx.string("XSendEvent failed to send the _NET_WM_MOVERESIZE client message to the X server");
    return cx.throw(err_msg);
}

x_flush(display);

x_close_display(display);

已尝试的参数取值

  • button:0、1
  • source_indication:0、1、2

环境信息

  • Electron版本:v41.3.0
  • 测试环境:
OS: EndeavourOS
SonicDE version: 6.6.4
SonicDE framework version: 6.25.0
Qt version: 6.11.0
Kernel version: 6.19.11-arch1-1 (64 位)
Desktop Environment: X11 (The X.Org Foundation) 1.21.1
CPU: 4 × Intel® Core™ i9-14900HX
RAM: 3.8 GiB available
GPU: llvmpipe
Vendor: VMware, Inc.
Product Name: VMware Virtual Platform
System Version: None

内容的提问来源于stack exchange,提问作者Yu Ling

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.01 20:17:29