electron_desktop_window_tree_host_linux.cc 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright (c) 2021 Ryan Gonzalez.
  2. // Use of this source code is governed by the MIT license that can be
  3. // found in the LICENSE file.
  4. // Portions of this file are sourced from
  5. // chrome/browser/ui/views/frame/browser_desktop_window_tree_host_linux.cc,
  6. // Copyright (c) 2019 The Chromium Authors,
  7. // which is governed by a BSD-style license
  8. #include "shell/browser/ui/electron_desktop_window_tree_host_linux.h"
  9. #include <vector>
  10. #include "base/feature_list.h"
  11. #include "base/i18n/rtl.h"
  12. #include "shell/browser/native_window_features.h"
  13. #include "shell/browser/ui/views/client_frame_view_linux.h"
  14. #include "ui/gfx/geometry/rect.h"
  15. #include "ui/gfx/geometry/skia_conversions.h"
  16. #include "ui/linux/linux_ui.h"
  17. #include "ui/ozone/public/ozone_platform.h"
  18. #include "ui/platform_window/platform_window.h"
  19. #include "ui/views/widget/desktop_aura/desktop_window_tree_host.h"
  20. #include "ui/views/widget/desktop_aura/desktop_window_tree_host_linux.h"
  21. #include "ui/views/window/non_client_view.h"
  22. namespace electron {
  23. ElectronDesktopWindowTreeHostLinux::ElectronDesktopWindowTreeHostLinux(
  24. NativeWindowViews* native_window_view,
  25. views::DesktopNativeWidgetAura* desktop_native_widget_aura)
  26. : views::DesktopWindowTreeHostLinux(native_window_view->widget(),
  27. desktop_native_widget_aura),
  28. native_window_view_(native_window_view) {}
  29. ElectronDesktopWindowTreeHostLinux::~ElectronDesktopWindowTreeHostLinux() =
  30. default;
  31. bool ElectronDesktopWindowTreeHostLinux::SupportsClientFrameShadow() const {
  32. return platform_window()->CanSetDecorationInsets() &&
  33. views::Widget::IsWindowCompositingSupported();
  34. }
  35. void ElectronDesktopWindowTreeHostLinux::OnWidgetInitDone() {
  36. views::DesktopWindowTreeHostLinux::OnWidgetInitDone();
  37. UpdateFrameHints();
  38. }
  39. void ElectronDesktopWindowTreeHostLinux::OnBoundsChanged(
  40. const BoundsChange& change) {
  41. views::DesktopWindowTreeHostLinux::OnBoundsChanged(change);
  42. UpdateFrameHints();
  43. if (ui::OzonePlatform::GetInstance()
  44. ->GetPlatformProperties()
  45. .electron_can_call_x11) {
  46. // The OnWindowStateChanged should receive all updates but currently under
  47. // X11 it doesn't receive changes to the fullscreen status because chromium
  48. // is handling the fullscreen state changes synchronously, see
  49. // X11Window::ToggleFullscreen in ui/ozone/platform/x11/x11_window.cc.
  50. UpdateWindowState(platform_window()->GetPlatformWindowState());
  51. }
  52. }
  53. void ElectronDesktopWindowTreeHostLinux::OnWindowStateChanged(
  54. ui::PlatformWindowState old_state,
  55. ui::PlatformWindowState new_state) {
  56. views::DesktopWindowTreeHostLinux::OnWindowStateChanged(old_state, new_state);
  57. UpdateFrameHints();
  58. UpdateWindowState(new_state);
  59. }
  60. void ElectronDesktopWindowTreeHostLinux::OnWindowTiledStateChanged(
  61. ui::WindowTiledEdges new_tiled_edges) {
  62. static_cast<ClientFrameViewLinux*>(
  63. native_window_view_->widget()->non_client_view()->frame_view())
  64. ->set_tiled_edges(new_tiled_edges);
  65. UpdateFrameHints();
  66. }
  67. void ElectronDesktopWindowTreeHostLinux::UpdateWindowState(
  68. ui::PlatformWindowState new_state) {
  69. if (window_state_ == new_state)
  70. return;
  71. switch (window_state_) {
  72. case ui::PlatformWindowState::kMinimized:
  73. native_window_view_->NotifyWindowRestore();
  74. break;
  75. case ui::PlatformWindowState::kMaximized:
  76. native_window_view_->NotifyWindowUnmaximize();
  77. break;
  78. case ui::PlatformWindowState::kFullScreen:
  79. native_window_view_->NotifyWindowLeaveFullScreen();
  80. break;
  81. case ui::PlatformWindowState::kUnknown:
  82. case ui::PlatformWindowState::kNormal:
  83. case ui::PlatformWindowState::kSnappedPrimary:
  84. case ui::PlatformWindowState::kSnappedSecondary:
  85. case ui::PlatformWindowState::kFloated:
  86. case ui::PlatformWindowState::kPinnedFullscreen:
  87. case ui::PlatformWindowState::kTrustedPinnedFullscreen:
  88. break;
  89. }
  90. switch (new_state) {
  91. case ui::PlatformWindowState::kMinimized:
  92. native_window_view_->NotifyWindowMinimize();
  93. break;
  94. case ui::PlatformWindowState::kMaximized:
  95. native_window_view_->NotifyWindowMaximize();
  96. break;
  97. case ui::PlatformWindowState::kFullScreen:
  98. native_window_view_->NotifyWindowEnterFullScreen();
  99. break;
  100. case ui::PlatformWindowState::kUnknown:
  101. case ui::PlatformWindowState::kNormal:
  102. case ui::PlatformWindowState::kSnappedPrimary:
  103. case ui::PlatformWindowState::kSnappedSecondary:
  104. case ui::PlatformWindowState::kFloated:
  105. case ui::PlatformWindowState::kPinnedFullscreen:
  106. case ui::PlatformWindowState::kTrustedPinnedFullscreen:
  107. break;
  108. }
  109. window_state_ = new_state;
  110. }
  111. void ElectronDesktopWindowTreeHostLinux::OnNativeThemeUpdated(
  112. ui::NativeTheme* observed_theme) {
  113. UpdateFrameHints();
  114. }
  115. void ElectronDesktopWindowTreeHostLinux::OnDeviceScaleFactorChanged() {
  116. UpdateFrameHints();
  117. }
  118. void ElectronDesktopWindowTreeHostLinux::UpdateFrameHints() {
  119. if (base::FeatureList::IsEnabled(features::kWaylandWindowDecorations)) {
  120. if (SupportsClientFrameShadow() && native_window_view_->has_frame() &&
  121. native_window_view_->has_client_frame()) {
  122. UpdateClientDecorationHints(static_cast<ClientFrameViewLinux*>(
  123. native_window_view_->widget()->non_client_view()->frame_view()));
  124. }
  125. SizeConstraintsChanged();
  126. }
  127. }
  128. void ElectronDesktopWindowTreeHostLinux::UpdateClientDecorationHints(
  129. ClientFrameViewLinux* view) {
  130. ui::PlatformWindow* window = platform_window();
  131. bool showing_frame = !native_window_view_->IsFullscreen();
  132. float scale = device_scale_factor();
  133. bool should_set_opaque_region = views::Widget::IsWindowCompositingSupported();
  134. gfx::Insets insets;
  135. gfx::Insets input_insets;
  136. if (showing_frame) {
  137. insets = view->GetBorderDecorationInsets();
  138. if (base::i18n::IsRTL()) {
  139. insets.set_left_right(insets.right(), insets.left());
  140. }
  141. input_insets = view->GetInputInsets();
  142. }
  143. const auto tiled_edges = view->tiled_edges();
  144. if (tiled_edges.left)
  145. insets.set_left(0);
  146. if (tiled_edges.right)
  147. insets.set_right(0);
  148. if (tiled_edges.top)
  149. insets.set_top(0);
  150. if (tiled_edges.bottom)
  151. insets.set_bottom(0);
  152. gfx::Insets scaled_insets = gfx::ScaleToCeiledInsets(insets, scale);
  153. window->SetDecorationInsets(&scaled_insets);
  154. gfx::Rect input_bounds(view->GetWidget()->GetWindowBoundsInScreen().size());
  155. input_bounds.Inset(insets + input_insets);
  156. window->SetInputRegion(std::optional<std::vector<gfx::Rect>>(
  157. {gfx::ScaleToEnclosingRect(input_bounds, scale)}));
  158. if (should_set_opaque_region) {
  159. // The opaque region is a list of rectangles that contain only fully
  160. // opaque pixels of the window. We need to convert the clipping
  161. // rounded-rect into this format.
  162. SkRRect rrect = view->GetRoundedWindowContentBounds();
  163. gfx::RectF rectf(view->GetWindowContentBounds());
  164. rectf.Scale(scale);
  165. // It is acceptable to omit some pixels that are opaque, but the region
  166. // must not include any translucent pixels. Therefore, we must
  167. // conservatively scale to the enclosed rectangle.
  168. gfx::Rect rect = gfx::ToEnclosedRect(rectf);
  169. // Create the initial region from the clipping rectangle without rounded
  170. // corners.
  171. SkRegion region(gfx::RectToSkIRect(rect));
  172. // Now subtract out the small rectangles that cover the corners.
  173. struct {
  174. SkRRect::Corner corner;
  175. bool left;
  176. bool upper;
  177. } kCorners[] = {
  178. {SkRRect::kUpperLeft_Corner, true, true},
  179. {SkRRect::kUpperRight_Corner, false, true},
  180. {SkRRect::kLowerLeft_Corner, true, false},
  181. {SkRRect::kLowerRight_Corner, false, false},
  182. };
  183. for (const auto& corner : kCorners) {
  184. auto radii = rrect.radii(corner.corner);
  185. auto rx = std::ceil(scale * radii.x());
  186. auto ry = std::ceil(scale * radii.y());
  187. auto corner_rect = SkIRect::MakeXYWH(
  188. corner.left ? rect.x() : rect.right() - rx,
  189. corner.upper ? rect.y() : rect.bottom() - ry, rx, ry);
  190. region.op(corner_rect, SkRegion::kDifference_Op);
  191. }
  192. // Convert the region to a list of rectangles.
  193. std::vector<gfx::Rect> opaque_region;
  194. for (SkRegion::Iterator i(region); !i.done(); i.next())
  195. opaque_region.emplace_back(gfx::SkIRectToRect(i.rect()));
  196. window->SetOpaqueRegion(opaque_region);
  197. }
  198. }
  199. } // namespace electron