拖动窗口左边框时无法消除抖动 [英] Can't get rid of jitter while dragging the left border of a window

查看:37
本文介绍了拖动窗口左边框时无法消除抖动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

截至 2015 年 3 月 26 日,问题已解决,请参阅本页底部,这是一个非常肮脏的伎俩.

截至 2014 年 8 月 18 日,部分解决:DWM 是罪魁祸首(见最后一条评论).

我使用 win32 API 构建了自己的窗口样式.在 Windows XP 和 Windows 7 下一切正常.然而,在 Windows 8 下会发生奇怪的事情:拖动窗口的左边框会导致右侧极度抖动,而它根本不应该移动.看看这个,你就会明白什么我是说.

当我拖动右边框时,左侧不会像应有的那样移动.诚然,有一些闪烁,但这是可以接受的.请参阅这个

我已经尝试了带有多个标志的 SetWindowPos()(begin/End)DeferWindowPos,但无济于事.即使使用 SWP_NOREDRAW 并阻止所有其他绘画也无济于事.无论是否有 CS_HREDRAWCS_VREDRAW.

我当然使用双缓冲.然而,摆脱那种讨厌的抖动似乎是不可能的.我还尝试了 Intel HD 4000 图形引擎的另一个驱动程序,但还是无济于事.我忽略了什么吗?是 Windows 8 的错误吗?

顺便说一下,当我打开拖动时显示窗口内容"选项(在高级系统设置菜单中)时,所有其他应用程序都显示相同的行为.

任何帮助将不胜感激.

干杯,埃德蒙.

PS:无法禁用拖动时显示窗口内容",因为它是我的应用程序的内置功能.<​​/p>

Edit1:似乎 d7samurai 是遇到同样的问题.

跟进:我尝试了很多方法来消除抖动,但没有任何帮助.问题是 W8 根本没有做它应该做的事情.以 SWP_NOREDRAW 标志为例.正如预期的那样,已移动的窗口侧的重新绘制被抑制,到目前为止一切顺利.但是......(窗户的)另一边仍然有效,这个被重新粉刷了!它不仅完全没有必要和无用,而且还用抖动重新绘制!此外,与 W7 和 XP 相比,绘画速度要慢两倍.在这个问题上花了整整一周后,我完全解决了 W8!这真的是一个 POC,负责这项拙劣工作的人精神错乱.真心希望W9能做的更好.阿门.

你应该抖动:

似乎上述异常不仅限于 W8.当 W7 设置为最佳外观"或至少设置为启用桌面组合"以及在窗口和按钮上使用视觉样式"时,也显示了相同的行为.然后我认为有可能完全不使用 SetWindowPos 函数来欺骗窗口,而是通过发送 ShowWindow( hWnd, SW_MAXIMIZE ) 命令并拦截 WM_GETMINMAXINFO msg 其中我指定所需的大小和位置.你猜怎么着?我还是有抖动.啊啊啊!!!

接下来要做什么?是否有可能在较低/较深的级别(挂钩?)拦截绘画以便以体面的方式重绘窗口?

更新 dd 03-26-2015,一个非常肮脏的把戏:

尤里卡!我终于找到了问题的解决方案.但首先,让我解释一下发生了什么以及为什么拖动会伴随着抖动.假设您要扩展窗口的左侧.与展开右侧相反,这是分两个步骤完成的.首先,原始大小的窗口内容向左移动.完成此操作后,内容将扩展到新右侧与先前右侧重合的程度.简而言之,它是移动"后跟调整大小"的组合,这就是导致丑陋抖动的原因.拖动窗口顶部时会发生类似的事情.在 Windows 8.1 或 10 中,您无能为力.遗憾的是,我还尝试了新的 DWM 函数(BufferedPaintInitBeginBufferedPaintEndBufferedPaintBufferedPaintUnInit),但没有有用.SetWindowPlacement() 也会产生抖动.显然,所有这些功能都会导致破坏调整大小的同一个罪魁祸首.然后我推断,当您创建一个新窗口并以新大小显示时,它根本不会抖动.相当明显,我会说.因此,反复创建一个新窗口并随后销毁前一个窗口,可能是一种解决方案,但这当然不是很有效.在玩这个想法时,我偶然发现,用新的/另一个尺寸显示隐藏的窗口,然后隐藏前一个窗口,也不会显示抖动.此过程比创建/销毁序列高效得多.所以在我的程序开始时,我只创建了两个窗口,第一个隐藏,第二个可见.下面的代码片段应该更详细地解释无抖动调整大小的过程:

........i=prm->hpi;//获取当前索引h1 = prm->parent[i];//获取当前句柄标志 = SWP_NOCOPYBITS;标志 |= SWP_NOSENDCHANGING;//如果 DWM 试图把它弄得一团糟://DWM 启用上边框右边框if(prm->parent[1] && ( rgn == TBE || rgn == LBE ) ){i = i + 1 &1;//获取新索引prm->hpi = i;//保存新索引h2 = prm->parent[i];//获取新的句柄prm->hParent = h2;//设置为当前标志 |= SWP_NOREDRAW;//绕过消息泵标志 |= SWP_SHOWWINDOW;//使 h2 可见SetWindowPos( h2, HWND_TOP, px, py, cx, cy, flags );PaintParent( h2 );//现在绘制DwmFlush();//等待完成ShowWindow( h1, SW_HIDE );//使 h1 不可见}//DWM 禁用或拖动右侧或底部边框:否则 SetWindowPos( h1, HWND_TOP, px, py, cx, cy, flags );

另外一件事:使用DwmFlush()确保新窗口的所有绘制完成,然后隐藏另一个窗口,否则会出现一些闪烁.当然,上面的过程比普通的要慢一点.在我的系统(带有 i5-3570K 处理器)上为 3..26 毫秒,具体取决于窗口的大小及其内容.但至少那种可怕的抖动已经消失了.

我确实意识到这是一个非常肮脏的把戏,所以如果有人知道更简洁的解决方案,请告诉我们.

解决方案

好像在 Windows 8.1 及以上,你必须配合 DWM 而不是绕着它走.

这意味着您必须通过 DwmGetCompositionTimingInfo 找出合成发生的时间表,并确保在适当的时刻将渲染事件放入队列中.您可能还会发现您需要在 WM_SIZING 事件之后立即重新无效并重新绘制窗口,或者甚至使用您自己的计时器来绘制比发送 WM_SIZING 事件更频繁的窗口.例如.如果 DWM 以 50 fps 的速度合成,但您每秒只能获得 WM_Sizing 事件,那么如果您将自己的计时器与 GetWindowPos 一起使用,您可能比依赖 WM_SIZING 更频繁地绘制.

您可能会发现在绘制之后明智地使用 DwmFlush 也可能有助于减少这种抖动.

As of 26 March 2015, problem solved, see bottom of this page, a very dirty trick.

As of 18 Aug 2014, partly solved: DWM is the culprit (see the last comment).

I've built my own window style using win32 API's. Everything went fine under windows XP and Windows 7. However, under windows 8 weird things happen: Dragging the left border of the window causes extreme jitter at the right side, while it shouldn't move at all. Take a look at this and you will understand what I mean.

When I drag the right border, the left side doesn't move as it should be. Admittedly, there is some flickering, but that's acceptable. See this

I've tried SetWindowPos() and (begin/End)DeferWindowPos with several flags, but to no avail. Even with SWP_NOREDRAW and blocking all other paintings doesn't help. Neither with or without CS_HREDRAW and CS_VREDRAW.

Of course I'm using double buffering. Yet It seems impossible to get rid of that pesky jitter. I've also tried another driver for the Intel HD 4000 graphic engine, again to no avail. Do I overlook something? Is it a bug of Windows 8?

By the way, when I switch on the "Show window contents while dragging" option (in the Advanced system settings menu), all other applications show the same behavior.

Any help will be greatly appreciated.

Cheers, Edmond.

PS: Disabling "Show window contents while dragging" is no option, because it's built in feature of my application.

Edit1: It seems that d7samurai is struggling with the same problem.

Follow up: I've tried a lot of things to get rid of the jitter, but nothing helps. The problem is that W8 simply doesn't do what it should do. Take for example the SWP_NOREDRAW flag. As expected, repainting of the window side that has been moved, is suppressed, So far so good. BUT.... the other side (of the window), which is still valid, this one gets repainted! It's not only totally needless and useless, but it is repainted with jitter! Moreover, painting is two times slower compared with W7 and XP. After spending a whole week on this problem, I'm through with W8, totally! It's really a POC and the guys who are responsible for this botch job are mentally deranged. I really hope that W9 will do a better job. Amen.

THOU SHALT JITTER:

It appeared that the anomalies described above are not limited to only W8. Also W7 showed the same behavior when it is set to "best appearance" or at least to "Enable desktop composition" together with "Use visual styles on windows and buttons". I then thought it might be possible to fool windows by not using the SetWindowPos function at all, rather by sending a ShowWindow( hWnd, SW_MAXIMIZE ) command and intercepting the WM_GETMINMAXINFO msg where I specify the desired size and position. Guess what? I still got jitter. Arghhh !!!

What to do next? Is there any possibility to intercept the painting at a lower/deeper level (hooks?) in order to redraw a window in a decent way?

Update dd 03-26-2015, a very dirty trick:

Eureka! I finally found the solution to the problem. But first, let me explain what is happening and why dragging is accompanied with jitter. Suppose you want to expand the left side of a window. Contrary to expanding the right side, this is done in two steps. First, the content of the window with its original size is moved to the left. After this has been done, the content is expanded to such an extent that the new right side coincides with the previous right side. In short it's combination of a "move" followed by a "resize" and this is what causes the ugly jitter. Similar things happen when you are dragging the top of a window. In Windows 8.1 or 10, there is nothing you can do against it. I've also tried the new DWM functions (BufferedPaintInit, BeginBufferedPaint, EndBufferedPaint, BufferedPaintUnInit) regrettably, to no avail. Also SetWindowPlacement() produces jitter. Apparently, all these functions lead to the same culprit which spoils a resize. I then reasoned that when you create a new window and display it with a new size, it doesn't jitter at all. Rather obvious, I would say. So repeatedly creating a new window and subsequently destroying the previous one, might be a solution, but this isn't very efficient, of course. While playing with this idea, I discovered by chance that making a hidden window visible with a new/another size, followed by hiding the previous window, also doesn't show jitter. This procedure is much more efficient than a create/destroy sequence. So at the start of my program I just created two windows, the first one hidden and the second one visible. The code snippet below should explain the procedure for jitterless resizing in more detail:

........
i = prm->hpi;                           // get current index
h1 = prm->parent[i];                    // get current handle
flags = SWP_NOCOPYBITS;
flags |= SWP_NOSENDCHANGING;
//  if DWM tries to make a mess of it:
//  DWM enabled         top border    right border
if( prm->parent[1] && ( rgn == TBE || rgn == LBE ) )
{
    i = i + 1 & 1;                      // get new index
    prm->hpi = i;                       // save new index
    h2 = prm->parent[i];                // get new handle
    prm->hParent = h2;                  // set as current one
    flags |= SWP_NOREDRAW;              // bypass message pump
    flags |= SWP_SHOWWINDOW;            // make h2 visible 
    SetWindowPos( h2, HWND_TOP, px, py, cx, cy, flags );
    PaintParent( h2 );                  // paint it now
    DwmFlush();                         // wait till finished
    ShowWindow( h1, SW_HIDE );          // make h1 invisible
}
// DWM disabled or dragging the right or bottom border:
else SetWindowPos( h1, HWND_TOP, px, py, cx, cy, flags );

One more thing: use DwmFlush() to ensure all painting of the new window is finished and then hide the other window, otherwise some flicker will be visible. Of course above procedure is a little slower than a regular one. On my system (with a i5-3570K processor) 3..26ms, depending on the size of the window and its content. But at least that horrible jitter has gone.

I do realize that it's a pretty dirty trick, so if someone knows a neater solution, please, let us know.

解决方案

It looks like under Windows 8.1 and above, you must cooperate with DWM rather than going around it.

This means that you have to find out the schedule of when composition takes place via DwmGetCompositionTimingInfo, and ensure that you slot your rendering events into the queue at appropriate moments. You may also find you need to re-invalidate and re-paint the window just after the WM_SIZING event, or even use your own timer to paint more often than the WM_SIZING events are sent. E.g. if DWM is compositing at 50 fps, but you are only getting then WM_Sizing events per second, then if you use your own timer with GetWindowPos you may be able to paint more often than if you rely on WM_SIZING.

You may find that judicious use of DwmFlush just after painting may also help reduce this jitter.

这篇关于拖动窗口左边框时无法消除抖动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆