Skip to content
Snippets Groups Projects

Draft: qt: use expanded client area for csd on windows

Open Fatih Uzunoğlu requested to merge fuzun/vlc:qt/windowsframelesswindowhint into master
4 unresolved threads

~~We already have client shadow, resize handle, button logic in the interface for other platforms. We can use them on Windows like we do on other platforms.

Going this way allows us to eliminate a lot of code, which is good for maintenance. At the same time, with frameless window hint, Windows 11 22H2 can use native acrylic backdrop filter (without artifacts unlike when this flag is not set).

We also need to have the resize handle outside the interface content, because there are interactive controls that the resize handle prevents the user to interact with them. For example, it is hard or impossible to scroll the play queue, or control the seek bar when it reaches the window frame (when frameless window hint is not set).

The biggest disadvantage of this seems to be not having the "aero snapping" feature. In my opinion, fixing the resize handle issue is more important than having extra features that are not essential.

Another disadvantage is having no DWM transitions. I'm not sure how important that is.

System menu is also recently factorized, so the system menu is still functional with frameless window hint.~~

Close #28906 (closed).

Request review @chub.

Draft because I'm not sure if it is completely reliable. It needs some more testing. I also need to check that the Windows 11 22H2 native backdrop blur does not reach to the margin area.

Edited by Fatih Uzunoğlu

Merge request reports

Members who can merge are allowed to add commits.

Merge request pipeline #545411 failed

Merge request pipeline failed for 1cdabfa2

Test coverage 17.61% (-0.05%) from 1 job
Approval is optional

Set by to be merged automatically when the pipeline succeeds

Ready to merge by members who can write to the target branch.

Merge details

  • The source branch is 1566 commits behind the target branch.
  • 3 commits will be added to master.
  • Source branch will be deleted.
  • Auto-merge enabled

Activity

Filter activity
  • Approvals
  • Assignees & reviewers
  • Comments (from bots)
  • Comments (from users)
  • Commits & branches
  • Edits
  • Labels
  • Lock status
  • Mentions
  • Merge request status
  • Tracking
  • Fatih Uzunoğlu changed the description

    changed the description

  • mentioned in issue #28906 (closed)

  • Pierre Lamot changed milestone to %4.0

    changed milestone to %4.0

    • It looks like we're loosing lots of things for a problem that continues to exists on other platforms.

      does WM_NCHITTEST allows to handle events when clicking in the shadow?

    • Author Reporter

      I have tried adjusting the window mask (https://doc.qt.io/qt-6/qwindow.html#setMask) as we are doing on Wayland to tell the compositor not deliver mouse/touch input in the shadow region, but it did not work. I'm not sure what can be done about that on Windows.

      As a side note, if I remember correctly I also tried that on X11, but it seems that setting a mask also clips the content (which is the usual behavior for X11, unlike Wayland).

    • Please register or sign in to reply
  • Fatih Uzunoğlu added 4 commits

    added 4 commits

    • 6c67c7b6 - qml: add and use `MainCtx::platformHandlesResizeWithCSD()`
    • 91beeae9 - contrib: qtbase: add patch `Windows QPA: improve Qt::FramlessWindowHint windows`
    • cd30c069 - qt: use frameless window hint for csd in `CompositorDirectComposition`
    • 3d62e035 - qt: use frameless window hint for csd in `CompositorWin7`

    Compare with previous version

    • Author Reporter

      With Windows QPA: improve Qt::FramlessWindowHint windows (https://github.com/qt/qtbase/commit/1a6ab689d5858e5997c989ce6c3574fb591438b0), frameless window hint has fewer limitations.

      This patch recently got reverted on Qt side because "Regression: FrameLessWindows have borders now when they should not.". Then reintroduced as a new flag as Qt::ExpandedClientAreaHint. For us, this is not too relevant at the moment, so we can probably use it.

      I could import the patch for Qt::ExpandedClientAreaHint instead, but I guess this would be a better idea because we are still using Qt 6.8, and that feature is supposed to come with Qt 6.9. When we use more recent Qt, we can switch using Qt::ExpandedClientAreaHint from Qt::FramelessWindowHint. I don't have strong opinion on this.

      Patch description:

      From a24517a47071debf5e53815835fa021c0d411eea Mon Sep 17 00:00:00 2001
      From: =?UTF-8?q?Timoth=C3=A9e=20Keller?= <timothee.keller@qt.io>
      Date: Thu, 15 Jun 2023 23:15:52 +0200
      Subject: [PATCH] Windows QPA: improve Qt::FramlessWindowHint windows
      MIME-Version: 1.0
      Content-Type: text/plain; charset=UTF-8
      Content-Transfer-Encoding: 8bit
      
      Currently, Qt::FramelessWindowHint windows lose functionalities that we
      might want them to keep. This patch attempts to address these by giving
      the frameless windows the following features:
      
      - Border-resizable: it is now possible to resize them by hovering over
      the edges of the (invisible) border.
      - Aero snap: While this patch does not enable dragging by clicking on
      non client areas, shortcuts like Win key + arrow keys let the user snap
      the windows as expected.
      - Animations/Shadows: minimizing/maximizing windows now has the typical
      animations, and the windows also have shadows.
      
      Change-Id: Icd7b671a6ac3dc300ba78378897b5dcae2432f76
      Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
      Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>

      Regarding the resize handle, this patch does not fix resize handle being within the client area. Windows does not seem to send WM_NCHITTEST to all windows unconditionally, it seems to first do a hit test itself. If the window is beneath the cursor, then the window receives it from what I understood. Another way is to capture the mouse (SetCapture()), but I'm not sure if we can do that.

      I will investigate more if we can have the resize handle outside the client area. But for now, that seems to be the only limitation of FramelessWindowHint thanks to that patch. I have already started using the patch here.

      I also need to check if this new FramelessWindowHint is problematic for the new Windows 11 native acrylic backdrop effect.

    • Author Reporter

      I also need to check if this new FramelessWindowHint is problematic for the new Windows 11 native acrylic backdrop effect.

      I can notice initial white background artifact with Nvidia (like before !6297 (merged)), so I will probably need to check for WS_POPUP and enable the Win11 22H2 native backdrop effect if it is really "frameless". Otherwise, we can trigger an initial resize on the interface to clear the artifact and use native effect, which would make it possible to use it also when CSD is disabled.

      We need to know if without WS_POPUP the native effect works (besides the initial artifact that can be cleared) with different GPUs.

      Edited by Fatih Uzunoğlu
    • Author Reporter

      I will investigate more if we can have the resize handle outside the client area

      I have done more investigation on this during the weekend. There are some discussions on how to achieve that, this one provided some insight to me: https://github.com/melak47/BorderlessWindow/issues/11.

      Restricting the client area in WM_NCCALCSIZE seems to be working, except it brings a prominent 1px (on Windows 10) and even bigger (on Windows 7) border. We can also not paint opaque color over that, because we have transparent parts, and also it is just a workaround and not a real fix.

      DWM seems to allow disabling painting in non-client region, but that also means no server side shadows, and possibly broken rounded corners.

      There seems to be a undocumented way to disable the border (https://github.com/melak47/BorderlessWindow/issues/13#issuecomment-309154142), which we can use on Windows 10. But it reportedly is not available on Windows 7. So, I guess for the moment we can fix #28906 (closed) only for Windows 10+. I guess the thick border on Windows 7 would be disturbing.

      An alternative could be using extended frame, and using our own shadow. But for that I need to use ExpandedClientAreaHint instead as setting custom margin does not work when FramelessWindowHint is set (I think Qt's patch should have adjusted that, because frameless window is not actually frameless with that patch, refer to why it was reverted), and I assume the window would not be rounded that way.

      In my opinion, we can go with the former approach: restrict the client area, and use undocumented ACCENT_ENABLE_GRADIENT to get rid of the border. The issue #28906 (closed) then would be limited to Windows 7, and 8 (?).

      By the way, starting with Windows 11 Build 22000, Windows seems to provide an official way to disable the border (https://learn.microsoft.com/en-us/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute) via adjusting DWMWA_BORDER_COLOR.

      Lastly, I think we should really not do what Office or Visual Studio is reportedly doing:

      Visual Studio 2012+ and Office 2013+ for example sidestep this by having multiple windows, a transparent window to which the shadow (or glow) is rendered (which also serves to capture the mouse and enable resizing outside the frame), and one or more "main" windows, with completely custom drawn title bars and window frames

      I wonder what is Firefox or Thunderbird is doing, as it seems working fine there.

    • You can also fix better for win10+ and then for Windows 7, have an old compatibility mode.

    • Some things I noticed while testing your branch

      • I have some garbage content at the edge (1px or something)
      • application buttons are doesn't scale with the application (they are really tiny here)
      • application buttons are drawn over the application border, that's especially visible with windows 11 round corner
      • application buttons are always drawn, so they remains visible in the playerview when the controlbare are hidden
      • when resizing garbage is visible in the un-refreshed area, current master seems to show the the acrylic layer (surface has a uniform look, that's less disturbing)
      • tiling menu is no longer available when hovering the maximize button
      • no resize animation

      Lastly, I think we should really not do what Office or Visual Studio is reportedly doing:

      So more or less the Win7 compositor solution?

    • Author Reporter

      I have some garbage content at the edge (1px or something)

      That occurs when the client size does not exceed to the window edges (Windows: ExpandedClientAreaHint fixups), but we need that for outside client area resizing that is handled by Windows. In a way, this can be considered extended frame, but Windows DWM adds a border.

      With the improved FramelessWindowHint (previous approach Windows QPA: improve Qt::FramlessWindowHint windows), it was a proper border. I guess that was because WVR_REDRAW was used. Qt's new approach does not seem to do that (https://github.com/qt/qtbase/commit/5feefd30734cd12753956819fc7c152be07c24cd#diff-3e09c31f0d04df4b66367538595a624aaa71179e67231c230835b76bdd79ca8bR1181 and https://github.com/qt/qtbase/commit/1a6ab689d5858e5997c989ce6c3574fb591438b0#diff-3e09c31f0d04df4b66367538595a624aaa71179e67231c230835b76bdd79ca8bR1134).

      We don't want a border there, even if it is a properly drawn border by the system compositor. But I think it is still better than resizing from inside.

      If we do extension ourselves, by handling resize and shadow in the client like we do with X11 and Wayland, then we would need to handle rounded corners ourselves as well. With layered window, that might be possible but I'm not really inclined on going this way.

      application buttons are doesn't scale with the application (they are really tiny here)

      I also noticed that, probably should be reported to Qt. Maybe we should not let Qt to draw the buttons but use our Qt Quick buttons. I did not do that because when Qt draws them itself with GDI, it also appears to be handling the hit testing (Currently we are doing that in the application).

      Worse, Qt's button handling is also off. I recently tried to fix that with our handling, ours is better than what Qt is doing. I don't think they properly tested ExpandedClientAreaHint at all. Sometimes the buttons trigger even when hovering, and the behavior does not match with Windows. Close button seems to trigger on release, minimize and maximize on press, and release within boundaries (standard Windows behavior) does not seem to be the case.

      Note that they started using QPainter 3 days ago instead of Gdi: https://github.com/qt/qtbase/commit/438aa1524ee99fd636dc02a7181857ade71bb101. I have not imported that patch here yet, but it says that it respects the DPI, so I guess it would not be tiny anymore.

      application buttons are drawn over the application border, that's especially visible with windows 11 round corner

      application buttons are always drawn, so they remains visible in the playerview when the controlbare are hidden

      Maybe we should draw the buttons ourselves and patch Qt to not draw them, but adjust the size so that Qt handles the hit testing for them.

      when resizing garbage is visible in the un-refreshed area, current master seems to show the the acrylic layer

      I think this is also about WVR_REDRAW (only when the client size does not extend to the whole window, so essentially with Windows: ExpandedClientAreaHint fixups). We can use it if that is the reason by patching Qt. Again, this was not a problem with the Windows QPA: improve Qt::FramlessWindowHint windows but it had other issues.

      tiling menu is no longer available when hovering the maximize button

      I noticed this, but have not investigated why. Qt seems to handle hit testing (https://github.com/qt/qtbase/blob/6e29a94b547fbafa69e97ac02aeb33edad63f2ae/src/plugins/platforms/windows/qwindowswindow.cpp#L3325), so this should work?

      no resize animation

      If you mean transitions, I have noticed this as well, but have not found out why. This was also fine with the Windows QPA: improve Qt::FramlessWindowHint windows. It uses WS_MINIMIZEBOX and WS_MAXIMIZEBOX which are necessary for DWM transitions (https://github.com/qt/qtbase/commit/1a6ab689d5858e5997c989ce6c3574fb591438b0#diff-3e09c31f0d04df4b66367538595a624aaa71179e67231c230835b76bdd79ca8bR816), but so does the new ExpandedClientAreaHint.

      There are other options there: WS_THICKFRAME, WS_CAPTION. If standard Qt window does not use these, maybe we can set ourselves if they are necessary for transitions (I'm not sure).

      So more or less the Win7 compositor solution?

      As far as I see, yes. If we really want to go that route, we should first consider extended frame (drawing shadow, buttons ourselves as well as handling the resize) and rounding the window corners ourselves with the layered window approach. Note that I'm not sure how layered top level window behaves when DirectComposition is involved, but theoretically it should work. I'm also not sure with FramelessWindowHint (which would be necessary with custom extended frame) we can have window transitions even if we set WS_MINIMIZEBOX and WS_MAXIMIZEBOX.

      Edited by Fatih Uzunoğlu
    • Author Reporter

      If you mean transitions, I have noticed this as well, but have not found out why.

      This seems to be an issue when Qt::WindowTitleHint is not used, which is probably about WS_CAPTION. I disabled Qt::WindowTitleHint because I did not want Qt (with the new expand client area) to draw the title itself.

    • Please register or sign in to reply
  • Fatih Uzunoğlu added 5 commits

    added 5 commits

    • 9c55019b - qml: add and use `MainCtx::platformHandlesTitleBarButtonsWithCSD()`
    • a8c18b7e - qml: add and use `MainCtx::platformHandlesShadowsWithCSD()`
    • e0cd6874 - contrib: qtbase: backport expanded client area support and have outside client area resize
    • d7629a45 - qt: use expanded client area for csd in `CompositorDirectComposition`
    • b1cc07d7 - qt: use expanded client area for csd in `CompositorWin7`

    Compare with previous version

  • Fatih Uzunoğlu added 94 commits

    added 94 commits

    • b1cc07d7...cdd4b633 - 87 commits from branch videolan:master
    • 8d099916 - qt: consider window margins in `CompositorDCompositionAcrylicSurface`
    • bcfddaf6 - qml: add and use `MainCtx::platformHandlesResizeWithCSD()`
    • 0552ccf0 - qml: add and use `MainCtx::platformHandlesTitleBarButtonsWithCSD()`
    • 272bf31a - qml: add and use `MainCtx::platformHandlesShadowsWithCSD()`
    • 64bfb761 - contrib: qtbase: backport expanded client area support and have outside client area resize
    • 03f66cf7 - qt: use expanded client area for csd in `CompositorDirectComposition`
    • 1cdabfa2 - qt: use expanded client area for csd in `CompositorWin7`

    Compare with previous version

  • Fatih Uzunoğlu changed title from Draft: qt: use frameless window hint for csd on windows to Draft: qt: use expanded client area for csd on windows

    changed title from Draft: qt: use frameless window hint for csd on windows to Draft: qt: use expanded client area for csd on windows

  • Fatih Uzunoğlu changed the description

    changed the description

    • Author Reporter

      I could import the patch for Qt::ExpandedClientAreaHint instead, but I guess this would be a better idea because we are still using Qt 6.8, and that feature is supposed to come with Qt 6.9. When we use more recent Qt, we can switch using Qt::ExpandedClientAreaHint from Qt::FramelessWindowHint. I don't have strong opinion on this.

      I decided to go that way instead. Qt::FramelessWindowHint is not properly integrated, there are existing code (such as invisible margins retrieval, or dark mode setting) that depend on frameless window (WS_POPUP), but the patch that improves the frameless window behavior does not seem to adjust them. I could manually change the checks to WS_POPUP instead of FramelessWindowHint, but I decided to not do that considering Qt also reverted that patch and that would cause issues with rebasing in the future.

      Now, Qt::ExpandedClientAreaHint is used instead. I have patched Qt myself to get the resize handle outside the client area. As said in the previous comment, there seems to be a border now:

      image

      image

      On Windows 10 at least, the border should appear dark if the color scheme is dark. I'm not sure why it is not the case, it may be a Qt bug. We can manually use the dwm function to adjust the dark mode if necessary.

      I will test this now on Windows 11.

    • Author Reporter

      I will test this now on Windows 11.

      It seems similar to Windows 10, there is a thin white border. Rounded corners and shadows seem fine.

    • Author Reporter

      It seems similar to Windows 10, there is a thin white border

      This seems to be because of using DwmExtendFrameIntoClientArea() (https://github.com/melak47/BorderlessWindow/issues/13#issuecomment-309234282). Qt windows have white background brush. However, I'm not sure why DwmExtendFrameIntoClientArea() is used, because WS_POPUP is not used, so the window should have shadows and rounded corners.

    • Please register or sign in to reply
  • Pierre Lamot mentioned in merge request !6562

    mentioned in merge request !6562

  • Fatih Uzunoğlu mentioned in merge request !6586 (merged)

    mentioned in merge request !6586 (merged)

Please register or sign in to reply
Loading