diff --git a/BUILD.gn b/BUILD.gn index d1a008347437..fe4fb706f557 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -670,6 +670,8 @@ source_set("electron_lib") { sources += [ "shell/browser/certificate_manager_model.cc", "shell/browser/certificate_manager_model.h", + "shell/browser/linux/x11_util.cc", + "shell/browser/linux/x11_util.h", "shell/browser/ui/gtk_util.cc", "shell/browser/ui/gtk_util.h", ] diff --git a/docs/api/base-window.md b/docs/api/base-window.md index 7ad17d045c90..c988ddb4b426 100644 --- a/docs/api/base-window.md +++ b/docs/api/base-window.md @@ -356,7 +356,7 @@ as `-webkit-app-region: drag` in a frameless window. Calling `event.preventDefault()` will prevent the menu from being displayed. -To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows). +To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows-linux). ### Static Methods diff --git a/docs/api/browser-window.md b/docs/api/browser-window.md index 95a1baa78445..aba24068dadf 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -435,7 +435,7 @@ as `-webkit-app-region: drag` in a frameless window. Calling `event.preventDefault()` will prevent the menu from being displayed. -To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows). +To convert `point` to DIP, use [`screen.screenToDipPoint(point)`](./screen.md#screenscreentodippointpoint-windows-linux). ### Static Methods diff --git a/docs/api/screen.md b/docs/api/screen.md index 4f68a2018b06..d923b8995f54 100644 --- a/docs/api/screen.md +++ b/docs/api/screen.md @@ -124,7 +124,7 @@ Returns [`Display`](structures/display.md) - The display nearest the specified p Returns [`Display`](structures/display.md) - The display that most closely intersects the provided bounds. -### `screen.screenToDipPoint(point)` _Windows_ +### `screen.screenToDipPoint(point)` _Windows_ _Linux_ * `point` [Point](structures/point.md) @@ -133,7 +133,10 @@ Returns [`Point`](structures/point.md) Converts a screen physical point to a screen DIP point. The DPI scale is performed relative to the display containing the physical point. -### `screen.dipToScreenPoint(point)` _Windows_ +Not currently supported on Wayland - if used there it will return the point passed +in with no changes. + +### `screen.dipToScreenPoint(point)` _Windows_ _Linux_ * `point` [Point](structures/point.md) @@ -142,6 +145,8 @@ Returns [`Point`](structures/point.md) Converts a screen DIP point to a screen physical point. The DPI scale is performed relative to the display containing the DIP point. +Not currently supported on Wayland. + ### `screen.screenToDipRect(window, rect)` _Windows_ * `window` [BrowserWindow](browser-window.md) | null diff --git a/shell/browser/api/electron_api_screen.cc b/shell/browser/api/electron_api_screen.cc index efee699f02bd..fed1141f25f8 100644 --- a/shell/browser/api/electron_api_screen.cc +++ b/shell/browser/api/electron_api_screen.cc @@ -20,11 +20,18 @@ #include "ui/display/display.h" #include "ui/display/screen.h" #include "ui/gfx/geometry/point.h" +#include "ui/gfx/geometry/point_conversions.h" +#include "ui/gfx/geometry/point_f.h" +#include "ui/gfx/geometry/vector2d_conversions.h" #if BUILDFLAG(IS_WIN) #include "ui/display/win/screen_win.h" #endif +#if BUILDFLAG(IS_LINUX) +#include "shell/browser/linux/x11_util.h" +#endif + #if defined(USE_OZONE) #include "ui/ozone/public/ozone_platform.h" #endif @@ -102,14 +109,6 @@ static gfx::Rect DIPToScreenRect(electron::NativeWindow* window, return display::win::GetScreenWin()->DIPToScreenRect(hwnd, rect); } -static gfx::PointF ScreenToDIPPoint(const gfx::PointF& pixel_point) { - return display::win::GetScreenWin()->ScreenToDIPPoint(pixel_point); -} - -static gfx::Point DIPToScreenPoint(const gfx::Point& dip_point) { - return display::win::GetScreenWin()->DIPToScreenPoint(dip_point); -} - #endif void Screen::OnDisplayAdded(const display::Display& new_display) { @@ -134,6 +133,44 @@ void Screen::OnDisplayMetricsChanged(const display::Display& display, MetricsToArray(changed_metrics))); } +gfx::PointF Screen::ScreenToDIPPoint(const gfx::PointF& point_px) { +#if BUILDFLAG(IS_WIN) + return display::win::GetScreenWin()->ScreenToDIPPoint(point_px); +#elif BUILDFLAG(IS_LINUX) + if (x11_util::IsX11()) { + gfx::Point pt_px = gfx::ToFlooredPoint(point_px); + display::Display display = GetDisplayNearestPoint(pt_px); + gfx::Vector2d delta_px = pt_px - display.native_origin(); + gfx::Vector2dF delta_dip = + gfx::ScaleVector2d(delta_px, 1.0 / display.device_scale_factor()); + return gfx::PointF(display.bounds().origin()) + delta_dip; + } else { + return point_px; + } +#else + return point_px; +#endif +} + +gfx::Point Screen::DIPToScreenPoint(const gfx::Point& point_dip) { +#if BUILDFLAG(IS_WIN) + return display::win::GetScreenWin()->DIPToScreenPoint(point_dip); +#elif BUILDFLAG(IS_LINUX) + if (x11_util::IsX11()) { + display::Display display = GetDisplayNearestPoint(point_dip); + gfx::Rect bounds_dip = display.bounds(); + gfx::Vector2d delta_dip = point_dip - bounds_dip.origin(); + gfx::Vector2d delta_px = gfx::ToFlooredVector2d( + gfx::ScaleVector2d(delta_dip, display.device_scale_factor())); + return display.native_origin() + delta_px; + } else { + return point_dip; + } +#else + return point_dip; +#endif +} + // static v8::Local Screen::Create(gin_helper::ErrorThrower error_thrower) { if (!Browser::Get()->is_ready()) { @@ -161,9 +198,11 @@ gin::ObjectTemplateBuilder Screen::GetObjectTemplateBuilder( .SetMethod("getPrimaryDisplay", &Screen::GetPrimaryDisplay) .SetMethod("getAllDisplays", &Screen::GetAllDisplays) .SetMethod("getDisplayNearestPoint", &Screen::GetDisplayNearestPoint) +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) + .SetMethod("screenToDipPoint", &Screen::ScreenToDIPPoint) + .SetMethod("dipToScreenPoint", &Screen::DIPToScreenPoint) +#endif #if BUILDFLAG(IS_WIN) - .SetMethod("screenToDipPoint", &ScreenToDIPPoint) - .SetMethod("dipToScreenPoint", &DIPToScreenPoint) .SetMethod("screenToDipRect", &ScreenToDIPRect) .SetMethod("dipToScreenRect", &DIPToScreenRect) #endif diff --git a/shell/browser/api/electron_api_screen.h b/shell/browser/api/electron_api_screen.h index cd5d7f5b5d25..4b63cd885889 100644 --- a/shell/browser/api/electron_api_screen.h +++ b/shell/browser/api/electron_api_screen.h @@ -15,6 +15,7 @@ namespace gfx { class Point; +class PointF; class Rect; class Screen; } // namespace gfx @@ -58,6 +59,9 @@ class Screen final : public gin::Wrappable, return screen_->GetDisplayMatching(match_rect); } + gfx::PointF ScreenToDIPPoint(const gfx::PointF& point_px); + gfx::Point DIPToScreenPoint(const gfx::Point& point_dip); + // display::DisplayObserver: void OnDisplayAdded(const display::Display& new_display) override; void OnDisplaysRemoved(const display::Displays& removed_displays) override; diff --git a/shell/browser/linux/x11_util.cc b/shell/browser/linux/x11_util.cc new file mode 100644 index 000000000000..38a33fe36d15 --- /dev/null +++ b/shell/browser/linux/x11_util.cc @@ -0,0 +1,17 @@ +// Copyright (c) 2025 Microsoft GmbH. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#include "shell/browser/linux/x11_util.h" + +#include "ui/ozone/public/ozone_platform.h" + +namespace x11_util { + +bool IsX11() { + return ui::OzonePlatform::GetInstance() + ->GetPlatformProperties() + .electron_can_call_x11; +} + +} // namespace x11_util diff --git a/shell/browser/linux/x11_util.h b/shell/browser/linux/x11_util.h new file mode 100644 index 000000000000..246d1b2aa2f5 --- /dev/null +++ b/shell/browser/linux/x11_util.h @@ -0,0 +1,14 @@ +// Copyright (c) 2025 Microsoft GmbH. +// Use of this source code is governed by the MIT license that can be +// found in the LICENSE file. + +#ifndef ELECTRON_SHELL_BROWSER_LINUX_X11_UTIL_H_ +#define ELECTRON_SHELL_BROWSER_LINUX_X11_UTIL_H_ + +namespace x11_util { + +bool IsX11(); + +} // namespace x11_util + +#endif // ELECTRON_SHELL_BROWSER_LINUX_X11_UTIL_H_ diff --git a/shell/browser/native_window_views.cc b/shell/browser/native_window_views.cc index dd1af72d13c1..0c5e0fc58f2c 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -53,6 +53,7 @@ #include "base/strings/string_util.h" #include "shell/browser/browser.h" #include "shell/browser/linux/unity_service.h" +#include "shell/browser/linux/x11_util.h" #include "shell/browser/ui/electron_desktop_window_tree_host_linux.h" #include "shell/browser/ui/views/client_frame_view_linux.h" #include "shell/browser/ui/views/native_frame_view.h" @@ -163,13 +164,6 @@ gfx::Size WindowSizeToContentSizeBuggy(HWND hwnd, const gfx::Size& size) { #endif -[[maybe_unused, nodiscard]] bool IsX11() { - static const bool is_x11 = ui::OzonePlatform::GetInstance() - ->GetPlatformProperties() - .electron_can_call_x11; - return is_x11; -} - class NativeWindowClientView : public views::ClientView { public: NativeWindowClientView(views::Widget* widget, @@ -331,7 +325,7 @@ NativeWindowViews::NativeWindowViews(const gin_helper::Dictionary& options, if (parent) SetParentWindow(parent); - if (IsX11()) { + if (x11_util::IsX11()) { // Before the window is mapped the SetWMSpecState can not work, so we have // to manually set the _NET_WM_STATE. std::vector state_atom_list; @@ -452,7 +446,7 @@ NativeWindowViews::~NativeWindowViews() { void NativeWindowViews::SetGTKDarkThemeEnabled(bool use_dark_theme) { #if BUILDFLAG(IS_LINUX) - if (IsX11()) { + if (x11_util::IsX11()) { const std::string color = use_dark_theme ? "dark" : "light"; auto* connection = x11::Connection::Get(); connection->SetStringProperty( @@ -514,7 +508,7 @@ void NativeWindowViews::Show() { // On X11, setting Z order before showing the window doesn't take effect, // so we have to call it again. - if (IsX11()) + if (x11_util::IsX11()) widget()->SetZOrderLevel(widget()->GetZOrderLevel()); #endif } @@ -530,7 +524,7 @@ void NativeWindowViews::ShowInactive() { // On X11, setting Z order before showing the window doesn't take effect, // so we have to call it again. - if (IsX11()) + if (x11_util::IsX11()) widget()->SetZOrderLevel(widget()->GetZOrderLevel()); #endif } @@ -575,7 +569,7 @@ bool NativeWindowViews::IsEnabled() const { #if BUILDFLAG(IS_WIN) return ::IsWindowEnabled(GetAcceleratedWidget()); #elif BUILDFLAG(IS_LINUX) - if (IsX11()) + if (x11_util::IsX11()) return !event_disabler_.get(); NOTIMPLEMENTED(); return true; @@ -612,7 +606,7 @@ void NativeWindowViews::SetEnabledInternal(bool enable) { #if BUILDFLAG(IS_WIN) ::EnableWindow(GetAcceleratedWidget(), enable); #else - if (IsX11()) { + if (x11_util::IsX11()) { views::DesktopWindowTreeHostPlatform* tree_host = views::DesktopWindowTreeHostLinux::GetHostForWidget( GetAcceleratedWidget()); @@ -944,7 +938,7 @@ bool NativeWindowViews::MoveAbove(const std::string& sourceId) { 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); #else - if (IsX11()) { + if (x11_util::IsX11()) { if (!IsWindowValid(static_cast(id.id))) return false; @@ -966,7 +960,7 @@ void NativeWindowViews::MoveTop() { size.width(), size.height(), SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW); #else - if (IsX11()) + if (x11_util::IsX11()) electron::MoveWindowToForeground( static_cast(GetAcceleratedWidget())); #endif @@ -1280,7 +1274,7 @@ void NativeWindowViews::SetIgnoreMouseEvents(bool ignore, bool forward) { SetForwardMouseMessages(forward); } #else - if (IsX11()) { + if (x11_util::IsX11()) { auto* connection = x11::Connection::Get(); if (ignore) { x11::Rectangle r{0, 0, 1, 1}; @@ -1401,7 +1395,7 @@ void NativeWindowViews::SetParentWindow(NativeWindow* parent) { NativeWindow::SetParentWindow(parent); #if BUILDFLAG(IS_LINUX) - if (IsX11()) { + if (x11_util::IsX11()) { auto* connection = x11::Connection::Get(); connection->SetProperty( static_cast(GetAcceleratedWidget()),