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

Linux Mint Cinnamon下通过X11句柄创建SFML 2.6.1窗口出现“无响应”提示但实际运行正常的解决方法咨询

Linux Mint Cinnamon下通过X11句柄创建SFML 2.6.1窗口出现“无响应”提示但实际运行正常的解决方法咨询

我之前在Cinnamon这类桌面环境下也碰到过一模一样的问题——明明窗口逻辑正常跑、事件也能正常处理,但系统就是硬标窗口“无响应”。核心原因其实是Cinnamon会定期通过X11的_NET_WM_PING客户端消息检测窗口存活状态,如果你的窗口没正确回复这个消息,桌面环境就会判定窗口挂掉,哪怕实际功能完全没问题。而i3这类平铺窗口管理器对这个检测的要求没那么严格,所以不会触发提示。

结合你的场景,我给你几个不用修改SFML源码就能解决的思路:

1. 处理Cinnamon的_NET_WM_PING消息,回复存活状态

这是最关键的一步。你需要在X11事件处理中主动捕获并回复WM的ping请求,明确告诉系统窗口还在正常运行。

修改步骤:

  • 首先在main函数中获取_NET_WM_PING对应的原子(X11中用来标识特定消息类型的标识符):
    Atom net_wm_ping = XInternAtom(disp, "_NET_WM_PING", False);
    
  • 然后完善你的checkX11Events函数,加入处理ping消息的逻辑:
    void checkX11Events(Display* disp, Window wind, Atom net_wm_ping){
        XEvent event;
    
        // 保留你原本的拖放事件处理逻辑
        while (XCheckTypedWindowEvent(disp, wind, SelectionNotify, &event)) {
            // 这里写你原本的拖放处理代码
        }
    
        // 新增处理WM的ping检测消息
        while (XCheckTypedWindowEvent(disp, wind, ClientMessage, &event)) {
            // 判断是否是_NET_WM_PING消息
            if (event.xclient.message_type == net_wm_ping && event.xclient.format == 32) {
                // 构造回复消息,告诉WM窗口正常存活
                XEvent reply;
                memset(&reply, 0, sizeof(reply));
                reply.xclient.type = ClientMessage;
                reply.xclient.window = wind;
                reply.xclient.message_type = net_wm_ping;
                reply.xclient.format = 32;
                reply.xclient.data.l[0] = event.xclient.data.l[0]; // 回传ping的序列号
                reply.xclient.data.l[1] = CurrentTime;
    
                // 发送回复并刷新X11消息队列
                XSendEvent(disp, RootWindow(disp, DefaultScreen(disp)), False, NoEventMask, &reply);
                XFlush(disp);
            }
            // 这里可以继续处理其他你需要的ClientMessage(比如拖放相关的)
        }
    }
    
  • 最后在主循环中调用这个函数时传入对应的参数:
    checkX11Events(disp, wind, net_wm_ping);
    

2. 检查窗口创建属性是否合理

你创建X11窗口时使用了CWOverrideRedirect标记,这个标记会让窗口绕过窗口管理器的常规管理。虽然i3对此兼容,但Cinnamon可能会对这类窗口的状态检测逻辑有差异。如果上面的方法无效,可以尝试去掉这个标记,修改窗口创建的属性掩码:

const Window wind = XCreateWindow(
    disp, RootWindow(disp, screen),
    0, 0, 800, 800, 0,
    DefaultDepth(disp, screen), InputOutput,
    DefaultVisual(disp, screen),
    CWBackPixel | CWEventMask, // 去掉CWOverrideRedirect
    &attributes);

3. 确保事件循环的完整性

要保证主循环中同时处理SFML事件和X11事件,并且定期调用XFlush(你已经在做了,但要确保不会遗漏)。SFML的pollEvent和X11的XCheckTypedWindowEvent都是非阻塞的,不会互相抢占,只要两者都被定期执行就没问题。

修改后的完整测试代码

#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Window/ContextSettings.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Window/WindowStyle.hpp>
#include <iostream>
#include <X11/X.h>
#include <X11/Xlib.h>
#include <cstdlib>
#include <unistd.h>
#include <cstring>

void checkX11Events(Display* disp, Window wind, Atom net_wm_ping){
    XEvent event;

    // 处理拖放相关事件(示例为空,你可以替换成自己的逻辑)
    while (XCheckTypedWindowEvent(disp, wind, SelectionNotify, &event)) {
        // 处理SelectionNotify事件
    }

    // 处理WM的ping检测消息
    while (XCheckTypedWindowEvent(disp, wind, ClientMessage, &event)) {
        if (event.xclient.message_type == net_wm_ping && event.xclient.format == 32) {
            XEvent reply;
            memset(&reply, 0, sizeof(reply));
            reply.xclient.type = ClientMessage;
            reply.xclient.window = wind;
            reply.xclient.message_type = net_wm_ping;
            reply.xclient.format = 32;
            reply.xclient.data.l[0] = event.xclient.data.l[0];
            reply.xclient.data.l[1] = CurrentTime;

            XSendEvent(disp, RootWindow(disp, DefaultScreen(disp)), False, NoEventMask, &reply);
            XFlush(disp);
        }
    }
}

void sfmlEventLoop(sf::RenderWindow& sf_window){
    sf::Event event;
    while (sf_window.pollEvent(event)){
        if (event.type == sf::Event::Closed){
            sf_window.close();
        } else if (event.type == sf::Event::MouseButtonPressed){
            std::cout << "Mouse pressed" << std::endl;
        }
    }
}

int main() {
    Display* disp = XOpenDisplay(nullptr);
    if (!disp) {
        exit(-1);
    }

    int screen = DefaultScreen(disp);
    XSetWindowAttributes attributes;
    attributes.background_pixel = BlackPixel(disp, screen);
    attributes.event_mask = KeyPressMask | StructureNotifyMask | PropertyChangeMask; // 适当增加需要的事件掩码

    // 创建窗口时去掉CWOverrideRedirect(可选,先尝试保留的情况,不行再去掉)
    const Window wind = XCreateWindow(
        disp, RootWindow(disp, screen),
        0, 0, 800, 800, 0,
        DefaultDepth(disp, screen), InputOutput,
        DefaultVisual(disp, screen),
        CWBackPixel | CWEventMask,
        &attributes);

    if (wind == 0) {
        exit(-1);
    }

    XStoreName(disp, wind, "Window Name");
    XMapWindow(disp, wind);
    XFlush(disp);

    // 获取_NET_WM_PING原子
    Atom net_wm_ping = XInternAtom(disp, "_NET_WM_PING", False);

    // 初始化SFML窗口
    sf::RenderWindow sf_window{wind, sf::ContextSettings()};

    // 主循环
    while(sf_window.isOpen()){
        checkX11Events(disp, wind, net_wm_ping);
        sfmlEventLoop(sf_window);
        
        sf_window.clear(sf::Color{35,35, 255, 255});
        sf_window.display();
        
        // 加个微小延迟,避免CPU占用过高(可选)
        usleep(1000);
    }

    XCloseDisplay(disp);
    return 0;
}

编译命令还是你原来的:

g++ main.cpp -lX11 -lsfml-graphics -lsfml-window

这个方案应该能解决Cinnamon下的无响应提示问题,本质就是让窗口正确响应桌面环境的存活检测。如果还有问题,可以检查事件掩码是否包含了足够的类型(比如PropertyChangeMask等),确保能接收到WM的消息。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 12:00:29