diff --git a/BUILD.gn b/BUILD.gn index a64f6f5715..1e5061a0c5 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 d59cdb2867..52e6deb288 100644 --- a/docs/api/base-window.md +++ b/docs/api/base-window.md @@ -362,7 +362,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 bb45135c16..e61faa458f 100644 --- a/docs/api/browser-window.md +++ b/docs/api/browser-window.md @@ -441,7 +441,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 1710bee406..c3bc334887 100644 --- a/docs/api/screen.md +++ b/docs/api/screen.md @@ -134,7 +134,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) @@ -143,7 +143,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) @@ -152,6 +155,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 1f8fa4828f..ef6d03e4b2 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 @@ -126,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::ScreenWin::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::ScreenWin::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()) { @@ -153,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", &display::win::ScreenWin::ScreenToDIPPoint) - .SetMethod("dipToScreenPoint", &display::win::ScreenWin::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 cd5d7f5b5d..4b63cd8858 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 0000000000..38a33fe36d --- /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 0000000000..246d1b2aa2 --- /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 bb96f1c1a7..2fae07f9cb 100644 --- a/shell/browser/native_window_views.cc +++ b/shell/browser/native_window_views.cc @@ -55,6 +55,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" @@ -164,13 +165,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, @@ -328,7 +322,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; @@ -479,7 +473,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( @@ -545,7 +539,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 } @@ -561,7 +555,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 } @@ -606,7 +600,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; @@ -643,7 +637,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()); @@ -975,7 +969,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; @@ -997,7 +991,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 @@ -1312,7 +1306,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}; @@ -1434,7 +1428,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()), @@ -1574,7 +1568,7 @@ bool NativeWindowViews::IsVisibleOnAllWorkspaces() const { return view_native_widget->IsVisibleOnAllWorkspaces(); #if BUILDFLAG(IS_LINUX) - if (IsX11()) { + if (x11_util::IsX11()) { // Use the presence/absence of _NET_WM_STATE_STICKY in _NET_WM_STATE to // determine whether the current window is visible on all workspaces. x11::Atom sticky_atom = x11::GetAtom("_NET_WM_STATE_STICKY");