feat: support dip <-> screen conversion on Linux X11 (#46895)

feat: support dip <-> screen conversion on Linux

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <shelley.vohr@gmail.com>
This commit is contained in:
trop[bot] 2025-05-02 19:08:50 -05:00 committed by GitHub
parent bf94f88569
commit d783f134d9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 106 additions and 31 deletions

View file

@ -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",
]

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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<v8::Value> 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

View file

@ -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<Screen>,
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;

View file

@ -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

View file

@ -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_

View file

@ -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<x11::Atom> 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<x11::Window>(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<x11::Window>(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<x11::Window>(GetAcceleratedWidget()),