After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 748872 - W32: Use layered windows
W32: Use layered windows
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Backend: Win32
unspecified
Other Windows
: Normal enhancement
: ---
Assigned To: gtk-win32 maintainers
gtk-bugs
Depends on: 761629
Blocks: 759826
 
 
Reported: 2015-05-04 07:34 UTC by LRN
Modified: 2016-03-02 21:46 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
GDK W32: Use layered windows (12.57 KB, patch)
2016-02-23 10:32 UTC, LRN
none Details | Review
GDK W32: Use layered windows (12.54 KB, patch)
2016-02-23 14:11 UTC, LRN
none Details | Review
GDK W32: Use layered windows (13.19 KB, patch)
2016-02-24 16:18 UTC, LRN
none Details | Review
GDK W32: Use layered windows (19.50 KB, patch)
2016-03-01 04:06 UTC, LRN
none Details | Review
GDK W32: Use layered windows (19.54 KB, patch)
2016-03-02 08:33 UTC, LRN
none Details | Review
GDK W32: Use layered windows (19.52 KB, patch)
2016-03-02 13:33 UTC, LRN
committed Details | Review

Description LRN 2015-05-04 07:34:50 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...
Comment 1 Ignacio Casal Quinteiro (nacho) 2015-12-11 14:12:03 UTC
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.
Comment 2 LRN 2015-12-11 17:03:26 UTC
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/
Comment 3 LRN 2016-02-23 10:32:41 UTC
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.
Comment 4 LRN 2016-02-23 14:11:07 UTC
Created attachment 321962 [details] [review]
GDK W32: Use layered windows

v2: rebased on top of newer version of patch for bug 761629
Comment 5 LRN 2016-02-24 16:18:51 UTC
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.
Comment 6 LRN 2016-02-27 01:01:22 UTC
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.
Comment 7 LRN 2016-03-01 04:06:44 UTC
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.
Comment 8 Fan, Chun-wei 2016-03-02 08:00:10 UTC
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.
Comment 9 LRN 2016-03-02 08:19:35 UTC
(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.
Comment 10 LRN 2016-03-02 08:33:36 UTC
Created attachment 322830 [details] [review]
GDK W32: Use layered windows

v5:
* Removed cruft left in gdk_win32_window_get_decorations()
Comment 11 Ignacio Casal Quinteiro (nacho) 2016-03-02 08:41:39 UTC
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
Comment 12 LRN 2016-03-02 13:33:24 UTC
Created attachment 322855 [details] [review]
GDK W32: Use layered windows

v6:
* Fixed a nitpick
Comment 13 LRN 2016-03-02 21:46:08 UTC
Attachment 322855 [details] pushed as c05f254 - GDK W32: Use layered windows