X11/GLX - 全屏模式?

17

我正在尝试创建一个 Linux 应用程序——这里是一个屏幕保护程序——但是发现很难找到有关如何将窗口全屏的简单任务的信息,即使是现有屏幕保护程序的代码也没有提到它们是如何管理的,我还没有看到任何像 XRemoveDecoration() 这样明显的函数。

在摸索了很多后,我使用了以下代码成功创建了与桌面相同大小的窗口:

Window win = DefaultRootWindow(disp);
XWindowAttributes getWinAttr;
XGetWindowAttributes(disp, win, &getWinAttr);
win = XCreateWindow(disp, win, 0, 0, getWinAttr.width, getWinAttr.height, 0, vInfo->depth, InputOutput, vInfo->visual, CWBorderPixel|CWColormap|CWEventMask|CWOverrideRedirect, &winAttr );

但这不能去掉标题栏和边框。显然有一种方法可以做到这一点,但我还没有找到任何指向该方向的内容,而不依赖于其他大型库(现有屏幕保护程序绝对不使用)。

编辑:请不要删除我帖子中的信息。我特意指出现有屏幕保护程序不使用可选库的原因非常充分,这是因为我已经分析了过去大部分时间的源代码。

我选择了最直接回答问题且适用于应用程序的答案。

如果你在研究xscreensavers时发现了这个问题...仍然适用相同的方法。 是的,xscreensaver有自己的API-它很复杂,并且实际上需要编写更多的代码行(是的,认真的)。 如果您想要在屏幕保护程序中使用OpenGL,您需要通过另一个 API(xlockmore,一个竞争系统)以及将其转换为xscreensaver的兼容性层。

但是,xscreensaver能够运行可以使用虚拟根窗口(查看vroot.h)作为屏幕保护程序的任何程序。所以我的建议就是这样做-您将拥有更多的控制权,没有限制API和更好的可移植性。 (我查看的一个示例甚至可以编译为Linux或Windows,使用相同的文件!)


7
+1显然已经做了一些研究。 - Flexo
请查看 https://dev59.com/L2TWa4cB1Zd3GeqPFJ45,对我来说,该答案是最好的且唯一有效的。 - TingQian LI
6个回答

13

一种方法是绕过窗口管理器:

XSetWindowAttributes wa;                                                     
wa.override_redirect = True;                                           
XCreateWindow( ..., &wa );

你可能需要使用GrabKeyboard来获取按键事件。 - eile
4
那个...居然有效。尽管我试图追踪你的步骤,几乎找不到任何与使用该切换按钮相关的资源,而且似乎没有任何 X 屏保程序具备它...但是,天哪,它确实有效! - DigitalMan
在最后第二个参数添加CWOverrideRedirect标志后,它可以工作,但坏消息是此后按键不再响应。 - TingQian LI
1
这个解决方案对我不起作用。窗口仍然与以前完全相同。它可以调整大小,顶部仍然有标题栏。 - MinuxLint

4

最好且更简单的方法是使用ICCCM规范中的atom,这对于大多数最新的窗口管理器都适用。只需使用以下代码:

Atom wm_state   = XInternAtom (display, "_NET_WM_STATE", true );
Atom wm_fullscreen = XInternAtom (display, "_NET_WM_STATE_FULLSCREEN", true );

XChangeProperty(display, window, wm_state, XA_ATOM, 32,
                PropModeReplace, (unsigned char *)&wm_fullscreen, 1);

如果您的窗口是透明的,那么只需在需要的地方使用XSetBackground()函数即可完成。


4

你缺少的信息是,屏幕保护程序并不负责全屏显示。屏幕保护守护进程会管理屏幕保护窗口,将其置于专用的屏幕保护窗口层中,并使其全屏显示。

因此,如果要编写屏幕保护程序,那么就没有问题了。但如果要编写全屏游戏,则必须设置覆盖重定向属性,以防止窗口被窗口管理器管理,并使其覆盖整个屏幕。


在默认的xscreensaver模块中似乎是这样,但OpenGL插件似乎普遍创建自己的窗口。 - DigitalMan
1
@DigitalMan:窗口创建!=窗口管理。实际上,xscreensaver守护进程就像一个窗口管理器,但只针对屏幕保护程序窗口。X-Screensaver扩展声明了一个额外的类似根窗口,它始终位于所有其他窗口的顶部,而屏幕保护守护进程负责将效果程序的窗口移动到那里。 - datenwolf

1

我发现freeglut全屏模式很好用,即使在其中托管基于着色器的OpenGL应用程序。这是被调用的内部代码(X11分支...)。希望对你有所帮助。

#define _NET_WM_STATE_TOGGLE    2
static int fghResizeFullscrToggle(void)
{
    XWindowAttributes attributes;

    if(glutGet(GLUT_FULL_SCREEN)) {
        /* restore original window size */
        SFG_Window *win = fgStructure.CurrentWindow;
        fgStructure.CurrentWindow->State.NeedToResize = GL_TRUE;
        fgStructure.CurrentWindow->State.Width  = win->State.OldWidth;
        fgStructure.CurrentWindow->State.Height = win->State.OldHeight;

    } else {
        /* resize the window to cover the entire screen */
        XGetWindowAttributes(fgDisplay.Display,
                fgStructure.CurrentWindow->Window.Handle,
                &attributes);

        /*
         * The "x" and "y" members of "attributes" are the window's coordinates
         * relative to its parent, i.e. to the decoration window.
         */
        XMoveResizeWindow(fgDisplay.Display,
                fgStructure.CurrentWindow->Window.Handle,
                -attributes.x,
                -attributes.y,
                fgDisplay.ScreenWidth,
                fgDisplay.ScreenHeight);
    }
    return 0;
}

static int fghEwmhFullscrToggle(void)
{
    XEvent xev;
    long evmask = SubstructureRedirectMask | SubstructureNotifyMask;

    if(!fgDisplay.State || !fgDisplay.StateFullScreen) {
        return -1;
    }

    xev.type = ClientMessage;
    xev.xclient.window = fgStructure.CurrentWindow->Window.Handle;
    xev.xclient.message_type = fgDisplay.State;
    xev.xclient.format = 32;
    xev.xclient.data.l[0] = _NET_WM_STATE_TOGGLE;
    xev.xclient.data.l[1] = fgDisplay.StateFullScreen;
    xev.xclient.data.l[2] = 0;  /* no second property to toggle */
    xev.xclient.data.l[3] = 1;  /* source indication: application */
    xev.xclient.data.l[4] = 0;  /* unused */

    if(!XSendEvent(fgDisplay.Display, fgDisplay.RootWindow, 0, evmask, &xev)) {
        return -1;
    }
    return 0;
}

static int fghToggleFullscreen(void)
{
    /* first try the EWMH (_NET_WM_STATE) method ... */
    if(fghEwmhFullscrToggle() != -1) {
        return 0;
    }

    /* fall back to resizing the window */
    if(fghResizeFullscrToggle() != -1) {
        return 0;
    }
    return -1;
}


#endif  /* TARGET_HOST_POSIX_X11 */

1

可以尝试查看以下示例:

将“Really Slick Screensavers”移植到GLX http://rss-glx.sourceforge.net/

请查看driver.c中的createWindow()函数。


不幸的是,这似乎与我尝试的并没有太大区别(缺少 override_redirect,最终起作用),而且在所发生的事情上似乎甚至比平常更少的评论。 - DigitalMan
@DigitalMan 请阅读INSTALL.xscreensaver。另外,请查看此FAQ http://www.jwz.org/xscreensaver/faq.html#popup-windows。就像datenwolf所发表的那样,听起来您正在创建自己的全屏应用程序,而不是插入到屏幕保护程序守护程序中。 - fdk1342

1

这绝对不难。你只需要按照这里所描述的,将正确的原子添加到正确的列表中即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接