Win32屏幕保护程序多显示器与主显示器不是最左边

Win32 Screensaver multiple monitors with main display not leftmost

本文关键字:显示器 左边 屏幕保护 程序 Win32      更新时间:2023-10-16

我做了一个屏幕保护程序,它可以简单地从右向左滚动用户定义的文本,如果超过左边界就自动跳转回右。

它可以完美地与多个显示器一起工作,除非有一个例外:如果"主显示器"在右边(即监视器#2是主要的),那么我就不会得到滚动文本,但是监视器被代码黑掉了。如果主显示为#1,则没有问题。

我花了几个小时研究代码,但无法确定问题出现在哪个阶段;我可以确认文本在正确的位置(我插入了验证其当前位置的日志代码),但就好像其中一个API调用只是擦除它一样。我已经阅读了它们的文档,看起来都很好。

我在WM_CREATE中创建一个自定义DC:

if (( hDC = CreateDC(TEXT("DISPLAY"), NULL, NULL, NULL)) == NULL )

为了防止闪烁,我创建了兼容的对象来更新:

void
TickerScreensaver::Paint_Prep(HDC hDC)
{
    _devcon_mem = CreateCompatibleDC(hDC);
    _devcon_orig = hDC;
    _bmp_mem = CreateCompatibleBitmap(hDC, _width, _height);
}

和在WM_PAINT中绘制时(在BeginPaint等之后),做一个位块传输到实际的设备上下文:

void
TickerScreensaver::Paint(HDC hDC, RECT rect)
{
    _bmp_orig = (HBITMAP)SelectObject(_devcon_mem, _bmp_mem);
    FillRect(_devcon_mem, &rect, (HBRUSH)GetStockObject(BLACK_BRUSH));
    if ( _gdiplus_token != NULL )
    {
        Graphics    graphics(_devcon_mem);
        SolidBrush  brush(cfg.display.font_colour); 
        FontFamily  font_family(cfg.display.font_family.c_str());
        Font        font(&font_family, cfg.display.font_size, FontStyleRegular, UnitPixel);
        PointF      point_f((f32)cfg.display.text_pos.x, (f32)cfg.display.text_pos.y);
        RectF       layout_rect(0, 0, 0, 0);
        RectF       bound_rect;
        graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
        graphics.MeasureString(cfg.display.text.c_str(), cfg.display.text.length(), &font, layout_rect, &bound_rect);
        cfg.display.offset.x = (DWORD)(0 - bound_rect.Width);
        cfg.display.offset.y = (DWORD)(bound_rect.Height / 2);
        graphics.DrawString(cfg.display.text.c_str(), cfg.display.text.length(), &font, point_f, &brush);
    }
    BitBlt(hDC, 0, 0, _width, _height, _devcon_mem, 0, 0, SRCCOPY);
    SelectObject(_devcon_mem, _bmp_orig);
}

我这样计算尺寸:

void
TickerScreensaver::GetFullscreenRect(HDC hDC, RECT *rect)
{
    RECT    s = { 0, 0, 0, 0 };
    if ( EnumDisplayMonitors(hDC, NULL, EnumMonitorCallback, (LPARAM)&s) )
    {
        CopyRect(rect, &s);
        s.left < 0 ?
            _width = s.right + (0 + -s.left) :
            _width = s.right;
        s.top < 0 ?
            _height = s.bottom + (0 + -s.top) :
            _height = s.bottom;
    }
}

请注意,计算出的宽度、高度等都是100%准确的;这纯粹是绘图代码,似乎没有在主显示器上工作,只有当它在右边时(它将原点设置为{0,0},监视器#1然后为负值)。它也可以在三屏显示器上再现,主体在中间。

好吧,事实证明这是很好的和简单的-在Paint(),我们应该使用一个矩形使用实际的宽度和高度,而不是一个检索到的包含负值(一个实际从API函数检索):

RECT r = { 0, 0, _width, _height }; 
_bmp_orig = (HBITMAP)SelectObject(_devcon_mem, _bmp_mem);
FillRect(_devcon_mem, &r, (HBRUSH)GetStockObject(BLACK_BRUSH));