Can't get rid of jitter while dragging the left border of a window
Asked Answered
P

3

2

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.

Parik answered 30/7, 2014 at 17:48 Comment(23)
What code are you using to re-size the window? My first impression is that you have a bug in setting the RECT as the buttons at the top right are cropped at times in the video. It may be a Win8 only bug, but, I'd look into the existing code first.Eddo
@Scott, the code is too long to put here. So I put only a few relevant pieces: ` GetWindowRect( hwnd, &wrc );` ` GetCursorPos( &pt );` ` py = wrc.top;` ` cy = wrc.bottom - wrc.top;` ` px = pt.x - 1 // define new position` ` cx = wrc.right - px; // define new width` ` flags = SWP_NOCOPYBITS; // | SWP_NOREDRAW;` ` SetWindowPos( hwnd, HWND_TOP, px, py, cx, cy, flags );` >" the buttons at the top right are cropped" The rest is aslo cropped. Please have a closer look. Besides, other applications, even the explorer, behaves just as bad.Parik
Sorry for above mess. I was unable to drop the code in a decent manner as "backtics" refuse to do what was promised.Parik
I'm not sure why you are subtracting a pixel from the x coordinate. Then you use that to adjust the width of the rect. That seems to be problematic.Eddo
As far as I know this is by design. As far as I remember this is caused because internaly first a move message is sent to the windows and then a resize. "Move" because the window coordinate x/y changes if you drag on the left side. In this moment the child windows seems to use old width/height. There should be an "move and resize at once" message to avoid this problem, but there isn't. I can not provide any sources for this theory at the moment.Gossipry
@rrirower, that's just a side-issue (and a little "anal"). In fact, any subtraction (or addition) will do. However, by subtracting 1 pixel the mouse is horizontally centered around the left border, which is 3 pixels thick. So it points to the 'middle' pixels of the border. Conversely, when dealing with the right border, 1 pixel is added. By the way, I just tested what happens without this kind of fine-tuning: still the same issue, i.e. jitter.Parik
Code, please. Don't add it as comments. Update the question, preferably with an SSCCE.Wiles
@bkausbk, I came to the same conclusion and this is also what you see on the 'movie'. BUT.... W-XP and W7 don't suffer from this "by design feature". It's W8 that makes a mess of it. Why for heavens sake? In the mean time I already tried a "resize then move" scheme, but got exactly the same (bad) result. Maybe the timing was incorrect and further experiments are needed.Parik
@IInspectable, I tried to drop the code, but after 1 hour I gave up. Apparently, the format of my text editor (Ultraedit) is incompatible with this site. Not that it really matters, as the code contains nothing exotic, just a few functions like GetWindowRect, GetCursorPos, fiddling a bit with size and position and then SetWindowPos. Besides it works under XP and W7. It's W8 that spoils it with its brilliant new "features". So there is something wrong with the settings of that OS, or I have to tackle this issues in a totally different way (and use different code and/or functions).Parik
@IInspectable, as for code, please have a look at: home.tiscali.nl/data.odyssey/files/CmdSizeWindow.cppParik
@Edmond: No the behaviour is the same in Windows XP, Windows Vista, Windows 7. May be it became even worser in Windows 8, but I didn't tested it so far.Gossipry
@ bkausbk: Yes you are right, that is, if we test it on common applications, for example the explorer. BUT... something weird is going on: The non-client area (right border and status bar) doesn't jitter as it should be, while the client area does show jitter. Very inconsequential, by the way. My app however, has been built in a different way, i.e around a bearbone WS_POPUP window. So it hasn't a non-client area etc. Furthermore, all paintings are double buffered. For this reason no jitter or flicker, at least under XP and W7, except W8 where it goes wrong (to be continued).Parik
I'm wondering whether the jitter stems from wrong info about the position of the right side (rc.right). I have to figure this out. Also, why has the NC area no issues?Parik
@ bkausbk: W8 is driving nuts. Even with CS_HREDRAW and CS_VREDRAW turned off in RegisterClassEx() and SWP_NOCOPYBITS | SWP_NOREDRAW turned on in SetWindowPos() and blocking all paintings in response of WM_PAINT, as expected, repainting is blocked during a resize attempt. But... I can still move my app. IOW, W8 -opposed to XP and W7- is ignoring the above flags! Maybe I'm overlooking something and I should build a SSCCE and see what happens then.Parik
@Edmond: I see the same effect even on the status bar. Since the border is non-client area that can not be drawn to from client area, you will not see such an effect there. CS_HREDRAW only causes a WM_PAINT to be sent during resizing with the mouse. As I said, this must be by design how window messages are generated. I don't see any solution for this problem using pure Windows API.Gossipry
@ bkausbk: Thanks for response. >" I don't see any solution for this problem using pure Windows" I'm afraid I have to agree with that. :-( I've intercepted/blocked all the messages during a resize and still the screen get painted (with jitter). Apparently, W8 bypasses the message pump.Parik
I see exactly the same issue with Notepad in Windows 7. It's not you, it's just how it is. In truth, I think what you see in your video is fine, and wouldn't worry me at all if I was a user. I think you are trying too hard!Landgrabber
@Ben: That's right and that is what you see with any "normal" program under any windows version (that is, if the "Show window contents while dragging" option is turned on, of course). But this does not apply to my application, which has above option permanently built in, so to speak. IOW, you can't disable it. As said before, it works perfectly under XP and W7. It is W8 that makes a mess of it which I can't stand. Each successive OS should be better instead of worse. Should I advice my customers to simply not use W8? (to be continued)Parik
@Ben: "You think the video is fine and wouldn't worry me at all if I was a user". Well, that's what my son also said, but I'm a perfectionist and these "minor details" really bother me. Moreover, my program itself has "perfectionistic pretensions" (it does some specialized calculations with extreme precision). For this reason, a sloppy looking GUI wouldn't be in line with the rest of the program. As outsiders can't look under the hood, they will judge on the appearance of the GUI. If it looks sloppy, they might think that the rest will be sloppy as well.Parik
I suspect you want to fiddle with the DWM api msdn.microsoft.com/en-us/library/windows/desktop/…Landgrabber
@Ben: Thanks for the hint. I'll have a look at it and keep you informed.Parik
@Ben: Indeed, dwmapi.dll is the culprit. When "Aero Glass" is enabled, I get jitter. When I select a basic desktop (via personalize), the jitter is gone. It's also possible to kill that Aero thingy with a call to DwmEnableComposition(0), thought that's not the most elegant way. Regrettably, this doesn't apply to W8, as DWM is always active. Many thanks for your hint. How can I vote for it?Parik
This is a very cool trick, but can you explain where you placed the code you pasted above? Since you are calling SetWindowPos yourself, how did you prevent Windows from also calling its normal internal SetWindowPos as the user drags the border?Landis
L
1

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.

Landgrabber answered 18/8, 2014 at 13:9 Comment(8)
Many thanks. I will start with DwnFlush and pray it will help. It will take some time as my compiler (Digital Mars from Walter Bright) doesn't support DWM stuff (yet). So I have to program it the hard way (i.e. importing). (BTW, I'm unable to vote for you hints because of < 15 rep. points. )Parik
PS: Any idea where to get dwmapi.lib and dwmapi.h?Parik
You can download the Platform SDK from Microsoft, msdn.microsoft.com/en-gb/windows/desktop/bg162891Landgrabber
That lib is not compatible with the Digital Mars C compiler. (-:Parik
@Edmond, I am not familiar with Digital Mars, but I am sure there must be a way to use the current Platform SDK. Probably you have to also use the Platform SDK Windows headers instead of those supplied with the DM compiler?Landgrabber
Hi Ben, After all, it's not a serious problem, as it's quite simple to import the DWM functions. BTW, DmwFlush() makes things worse. Maybe DwmExtendFrameIntoClientArea() helps. The point is that the non-client area doesn't suffer from jitter. Making the client area a non-client area might be a solution.Parik
DwmExtendFrameIntoClientArea() Doesn't cure the issues.Parik
This is a super cool idea. I implemented this idea and although it is not a 100% solution, it does greatly reduce tearing due to DWM. See source code at: #53000791Landis
P
0

I found a solution for the problem. Please, see the latest update of the original message.

Parik answered 26/3, 2015 at 17:11 Comment(0)
L
0

That two-window solution is very creative! I don't think I can apply that solution because my window is an OpenGL window with heavy state that would have to be maintained/replicated across window instances.

I think I can provide some more insight about why you kept seeing that jitter despite messing with all the usual suspects like SWP_NOREDRAW and SWP_NOCOPYBITS.

I ran into the same jitter problem and discovered that since apps under Windows 8/10 Aero don't draw directly to the screen, but rather draw to offscreen buffers that are then composited by the evil DWM.exe window manager, it turns out that DWM actually adds another layer of BitBlt-type behavior on top of the existing legacy XP/Vista/7 BitBlt behavior.

And the DWM blit behavior is even more crazy because they don't just copy the client area, but they actually replicate pixels at the edges of your old client area to make the new one. Unfortunately, making DWM not do its blit is much harder than just passing some extra flags.

It seems like your two-window trick fools DWM into not doing the extra blit, but I am looking for a way to accomplish that with one window.

I don't have a 100% solution but I hope the above info will help, and I do have code for an implementation of Ben's cool idea to time drawing to minimize the chances of DWM doing its blit:

How to smooth ugly jitter/flicker/jumping when resizing windows, especially dragging left/top border (Win 7-10; bg, bitblt and DWM)?

Enjoy!

Landis answered 26/10, 2018 at 8:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.