GNOME Bugzilla – Bug 748872
W32: Use layered windows
Last modified: 2016-03-02 21:46:15 UTC
Right now GTK uses normal, oldschool window-drawing routine: waits for WM_PAINT, calls BeginPaint(), draws, calls EndPaint(), and that's it. OK, actually, GDK complicates this a bit. Cursory examination of the code did not reveal all the details, but it looks like an intricate dance between GDK processing WM events and sending GDK_EXPOSE to gtkmain, which in turn calls gdk_window_begin_paint_region(), redraws widgets and calls gdk_window_end_paint(). Anyway, there's an alternative mechanism for drawing a window: layered windows. Layered windows never receive WM_PAINT. Instead the application itself is responsible for figuring out when the window needs repainting (such as response to user actions, and also on a timer for animations), and doing so with (eventually) a call to UpdateLayeredWindow(). The upshots are: 1) Alpha-blending support is built-in. No need for DWM and all the weirdness about blurbehind not working in some cases (better W8 compatibility). Also, this, to my surprise, works with cairo_win32_surface_create_with_ddb() (available since cairo 1.4!) just fine (so much for cairo_win32_surface_create_with_format() in 1.14.3...). 2) Better interaction with WM. WM will just keep the last updated version of the image around and draws it again, for example, when something else is moving on top of it, obscuring and then revealing its contents (this actually works regardless of what you do, as long as desktop composition is enabled, AFAICS; in case it isn't though, layered windows will supposedly still have this going, while normal WM_PAINT-painted ones will not). 3) Doesn't need composition to work. Downsides: 1) Changing this means delving into the guts of GDK/GTK. Who knows what i could break if i do this. 2) Layered windows are drawn by application and only by application. That includes window decorations. Which means that we need to either write out own code that draws normal WM-lookalike decorations (W32 theming API should make this possible, just might take a lot of code), or make *all* windows CSDed unconditionally (given that this won't need composition, unconditional CSD is not as bad as it would otherwise be). Not sure what the users' reaction would be though...
Did you work out a prototype for this? I have to admit that doing the CSD ourselves for the normal case is a bit scary though.
No, i don't have any code for this, only theories. Yes, it is kind of scary to reimplement the whole window-frame-titlebar thingy. There might be another way - Windows 8 added a feature where you can make layered /child/ windows. Theoretically, this would allow us to create a normal non-layered toplevel window, with frame and all, and then just create a single child window that /is/ layered, fills the whole client area, and we do what we want there. There's an article[1] that indicates that this does work, but the author notes that performance is awful. Granted, he's creating 100 randomly-placed overlapping child windows, so maybe this won't affect us, since we'll have just one child window. Obviously, this would be windows-8-or-newer only, whereas GTK is just Vista-or-newer, and will remain that way for quite some time, i expect. [1] http://habrahabr.ru/post/247397/
Created attachment 321932 [details] [review] GDK W32: Use layered windows Makes toplevel windows use true layered windows. This only works correctly with CSD. Non-csd windows end up with no decorations, because W32 doesn't draw its own decorations for layered windows.
Created attachment 321962 [details] [review] GDK W32: Use layered windows v2: rebased on top of newer version of patch for bug 761629
Created attachment 322255 [details] [review] GDK W32: Use layered windows v3: * Rebased again * Call _gdk_win32_adjust_client_rect() to ensure that cache surface covers the whole window, not just its client area. Otherwise the surface is too small and UpdateLayeredWindow() does not redraw the window, when non-CSD windows are made layered. Note that non-CSD windows are still rendered and handled incorrectly, this just makes them visible again.
Poked around. Turns out, it's literally impossible for us to draw Aero Glass all by ourselves (it's a private feature of W32 WM). My best effort would look roughly like Company's win32 theme. I mean, i'm using the same theme parts he has. And don't get me started on Metro crap. So right now i'm leaning towards simply mandating CSD for layered windows (or vice versa - mandating layered windows for CSD; not sure about switching CSD/layered on and off at runtime, is that needed?). It wouldn't be any worse than trying to draw window frame myself in GDK.
Created attachment 322718 [details] [review] GDK W32: Use layered windows v4: * Don't use gobject data to store decorations * Detect CSDiness by looking at window decorations * Use layered windows for CSD windows (unless forced not to) * Layeredness is enabled/disabled in update_style_bits. Hopefully, this will allow us to enable/disable it at runtime. We definitely don't need to have it set at window creation only. I think this is good enough for consideration, especially with bug 759899. And with bug 759826 it will finally make CSD uncontroversial (possible bugs aside). Now, there may be side-effects to using layered windows, but we won't know that until we give this code for people to test on different systems. I'm planning to test it on Windows 10 soon.
Review of attachment 322718 [details] [review]: Hi LRN, An initial look here seems quite nice to me, thanks! There is one issue though... With blessings, thank you! ::: gdk/win32/gdkwindow-win32.c @@ +2769,3 @@ + impl = GDK_WINDOW_IMPL_WIN32 (window->impl); + + decorations_set = impl->decorations != NULL; Hmm, this line is going to cause a compiler warning/error due to type mismatch... We could just do decorations_set = impl->decorations here, since we are going to check for the NULL very soon.
(In reply to Fan, Chun-wei from comment #8) > Review of attachment 322718 [details] [review] [review]: > > ::: gdk/win32/gdkwindow-win32.c > @@ +2769,3 @@ > + impl = GDK_WINDOW_IMPL_WIN32 (window->impl); > + > + decorations_set = impl->decorations != NULL; > > Hmm, this line is going to cause a compiler warning/error due to type > mismatch... > > We could just do decorations_set = impl->decorations here, since we are > going to check for the NULL very soon. Yeah, no, this is obviously wrong, and i've already fixed that in my local copy of the source tree, just forgot to add that to the commit.
Created attachment 322830 [details] [review] GDK W32: Use layered windows v5: * Removed cruft left in gdk_win32_window_get_decorations()
Review of attachment 322830 [details] [review]: More nitpicking from my side. That said I think we can get this in. ::: gdk/win32/gdkwindow-win32.c @@ +2618,3 @@ + */ + if (_gdk_win32_window_lacks_wm_decorations (window)) + impl->layered = g_strcmp0 (g_getenv ("GDK_WIN32_LAYERED"), "0") == 0 ? FALSE : TRUE; == 0 already returns a boolean, no need for ? FALSE: TRUE, just do != 0
Created attachment 322855 [details] [review] GDK W32: Use layered windows v6: * Fixed a nitpick
Attachment 322855 [details] pushed as c05f254 - GDK W32: Use layered windows