X11,改变分辨率并使窗口全屏

3
我很头疼这个问题。我正通过以下方式程序地改变屏幕的分辨率:
int FindBestVideoMode(int screen, unsigned int &width, unsigned int &height)
{
    int modeCount;
    XF86VidModeModeInfo** modes;

    if (XF86VidModeGetAllModeLines(display, screen, &modeCount, &modes))
    {
        int bestMode  = -1;
        int bestMatch = INT_MAX;
        for(int i = 0; i < modeCount; i ++)
        {
            int match = (width  - modes[i]->hdisplay) *
                        (width  - modes[i]->hdisplay) +
                        (height - modes[i]->vdisplay) *
                        (height - modes[i]->vdisplay);

            if(match < bestMatch)
            {
                bestMatch = match;
                bestMode  = i;
            }
        }

        width  = modes[bestMode]->hdisplay;
        height = modes[bestMode]->vdisplay;

        XFree(modes);

        return bestMode;
    }

    return -1;
}

void SwitchVideoMode(int screen, int mode)
{
    if (mode >= 0)
    {
        int modeCount;
        XF86VidModeModeInfo** modes;

        if (XF86VidModeGetAllModeLines(display, screen, &modeCount, &modes))
        {
            if (mode < modeCount)
            {
                XF86VidModeSwitchToMode(display, screen, modes[mode]);
                XF86VidModeSetViewPort(display, screen, 0, 0);


                XFlush(display);
            }

            XFree(modes);
        }
    }
}

void SwitchToBestVideoMode(int screen, unsigned int &width, unsigned int &height)
{
    SwitchVideoMode(screen, FindBestVideoMode(screen, width, height));
}

void RestoreVideoMode(int screen)
{
    auto iVideoMode = DefaultVideoModes.Find(screen);
    if (iVideoMode != nullptr)
    {
        XF86VidModeSwitchToMode(display, screen, &iVideoMode->value);
        XF86VidModeSetViewPort(display, screen, 0, 0);

        XFlush(display);
    }
}

这部分工作很好。然后我会使用以下方法将窗口切换到全屏模式:

XEvent e;
e.xclient.type         = ClientMessage;
e.xclient.window       = window;
e.xclient.message_type = _NET_WM_STATE;
e.xclient.format = 32;
e.xclient.data.l[0] = 2;    // _NET_WM_STATE_TOGGLE
e.xclient.data.l[1] = XInternAtom(display, "_NET_WM_STATE_FULLSCREEN", True);
e.xclient.data.l[2] = 0;    // no second property to toggle
e.xclient.data.l[3] = 1;
e.xclient.data.l[4] = 0;

XSendEvent(display, DefaultRootWindow(display), False, SubstructureRedirectMask | SubstructureNotifyMask, &e);
XMoveResizeWindow(display, window, 0, 0, width, height);

现在的问题是,窗口的大小被设置为桌面分辨率的大小,而不是在进行编程分辨率更改时设置的新分辨率大小。我期望的,也确实是我想要的,是窗口的大小与新分辨率相同。
我猜测我可能只是对这个问题有些简单的误解,但我非常感谢对此的任何想法。我不想使用像SDL这样的外部库。
谢谢!

如果您没有使用XF86VidModeSwitch或XRandr切换显示模式,那么您仍然处于桌面分辨率,并且只是创建一个没有WM装饰并将其放置在左上角的窗口。当您需要与桌面相同的分辨率时,这很好-您可以自由切换窗口并在此窗口上方有其他窗口,但这并不是完美的解决方案。 - keltar
如果您不想使用SDL,仍然可以阅读SDL源代码来了解它是如何实现的,并在您的项目中复制该部分代码。 - Jan Hudec
1个回答

3
你遇到的问题是,你依赖于窗口管理器来正确放置你的窗口。不幸的是,并不是所有的窗口管理器都关心XF86VidMode或RandR。在视频模式更改后创建全屏窗口的规范解决方案是将窗口创建为无边框和“覆盖重定向”,以便它不被WM管理,然后明确地将其定位以覆盖从(0,0)到(vidmode宽度,vidmode高度)的区域。

@HavocP:现在XF86VidMode的支持甚至比RandR还要少。而且你可能不信,在一些全屏应用程序中,比如游戏,你实际上希望防止其他程序捕获热键(还记得那些可恶的Windows键问题吗?玩家们把那个键从键盘上拔掉了,有人吗?)。堆叠问题可以通过在屏幕保护程序或组合层中创建窗口来解决。 - datenwolf

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