Repository: electron/electron (language: cpp)

Issue:
[Bug]: context menu on Ubuntu/x11 in the top left of the screen on 29.0.0-beta.3
### Preflight Checklist

- [X] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [X] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a bug report that matches the one I want to file, without success.

### Electron Version

29.0.0-beta.3

### What operating system are you using?

Ubuntu

### Operating System Version

Ubuntu 23.04

### What arch are you using?

x64

### Last Known Working Electron version

29.0.0-beta.2

### Expected Behavior

The context menu is located next to the pointer cursor, screenshot from 29.0.0-beta.2

![Screenshot from 2024-01-26 16-12-40](https://github.com/electron/electron/assets/41970/c46af8ab-7242-4272-971f-dc47c1b3d89a)


### Actual Behavior

The context menu appears in the top left of the screen when using 29.0.0-beta.3
![Screenshot from 2024-01-26 16-13-01](https://github.com/electron/electron/assets/41970/abe2d5dc-7f91-40a3-b105-59553badd1ad)


### Testcase Gist URL

https://gist.github.com/ad3013a440b58351f3c496923a0ea2d6

### Additional Information

This is using X11


Discussion / hints:
Still present in beta.4 and beta.5 as well.
https://github.com/electron/electron/compare/v29.0.0-beta.2...v29.0.0-beta.3

Haven't tracked down the root cause, but here's a breadcrumb:

In [`MenuViews::PopupAt()`](https://github.com/electron/electron/blob/9daf416097266fb4d226eb40d5c8d2ea5b033e34/shell/browser/api/electron_api_menu_views.cc#L34),

```c++
  // (-1, -1) means showing on mouse location.
  gfx::Point location;
  if (x == -1 || y == -1) {
    location = display::Screen::GetScreen()->GetCursorScreenPoint();
  } else {
    gfx::Point origin = native_window->GetContentBounds().origin();
    location = gfx::Point(origin.x() + x, origin.y() + y);
  }
```

`display::Screen::GetScreen()->GetCursorScreenPoint()` is returning meaningful values in 29.0.0-beta.2, but `0, 0` in beta.3.
`X11ScreenOzone::GetCursorScreenPoint()` is returning 0,0 (which it gets from `ui::X11EventSource::GetInstance()->last_cursor_location()`) after for us after https://chromium-review.googlesource.com/c/chromium/src/+/5214873 .

Though the function _does_ work in its invocations up until the moment we create a popup, so it may  be that we need to change the order of our actions when creating a popup :thinking: 
Just leaving more breadcrumbs: I noticed there had been some followup changes to the upstream `ui/gfx/` code so I tested out the gist Electron `main` -- expecting it to work, so that I could find the right commit to cherry-pick back to e29 -- but it's still broken in `v30.0.0-nightly.20240207`
One workaround is to pass in the same `x, y` that came from the `context-menu` props:

```js
  mainWindow.webContents.on('context-menu', (event, props) => {
    const { x, y } = props;
    const template = [ 
      { label: 'Edit', }, 
      { label: 'Hello From Electron!' }
    ]
    Menu.buildFromTemplate(template).popup({ window: mainWindow, x, y });
  });
```

That avoids the broken lookup.
Every 0,0 that I'm seeing is coming from `GetRootCursorLocationFromEvent()` in `ui/events/platform/x11/x11_event_source.cc` returning `0,0` whenever it receives an `x11::Input::CrossingEvent`.

```c++
    } else if (crossing &&
               (crossing->opcode == x11::Input::CrossingEvent::Enter ||
                crossing->opcode == x11::Input::CrossingEvent::Leave)) {
      is_valid_event = touch_factory->ShouldProcessCrossingEvent(*crossing);
      std::cerr << __FILE__ << ':' << __LINE__ << " is valid" << std::endl;
    }
```

It gets the value from `EventSystemLocationFromXEvent()` which returns root_x, root_y for CrossingEvents:

```c++
  gfx::Point EventSystemLocationFromXEvent(const x11::Event& xev) {
    if (auto* crossing = xev.As<x11::CrossingEvent>())
      return gfx::Point(crossing->root_x, crossing->root_y);
    if (auto* button = xev.As<x11::ButtonEvent>())
      return gfx::Point(button->root_x, button->root_y);
    if (auto* motion = xev.As<x11::MotionNotifyEvent>())
      return gfx::Point(motion->root_x, motion->root_y);
    if (auto* xievent = xev.As<x11::Input::DeviceEvent>()) {
      return gfx::Point(Fp1616ToDouble(xievent->root_x),
                        Fp1616ToDouble(xievent->root_y));
    }
    return gfx::Point();
  }
```

And this seems pretty reasonable. https://tronche.com/gui/x/xlib/events/window-entry-exit/  describes those fields:

> The x_root and y_root members are set to the pointer's coordinates relative to the root window's origin at the time of the event.
OK I found it. It's an upstream bug.

There are two different CrossingEvent classes: `x11::Input::CrossingEvent` and `x11::CrossingEvent`.

In `ui/events/platform/x11/x11_event_source.cc` , `GetRootCursorLocationFromEvent()` calls  `ui::EventSystemLocationFromXEvent()` on an event if it's the **former**. But `EventLocationFromXEvent()` igores that type and looks for the **latter**.

IDK why there are two types but this mismatch is the root of the problem & it can be resolved by checking for `x11::Input::CrossingEvent` in `EventTimeFromXEvent()`. We can add a `patches/chromium/` patch to fix this for e29 and submit a fix upstream.

Fix patch:
diff --git a/patches/chromium/.patches b/patches/chromium/.patches
index 89d22941f0860..7b76ce5b0374f 100644
--- a/patches/chromium/.patches
+++ b/patches/chromium/.patches
@@ -127,3 +127,4 @@ fix_restore_original_resize_performance_on_macos.patch
 feat_allow_code_cache_in_custom_schemes.patch
 build_run_reclient_cfg_generator_after_chrome.patch
 fix_suppress_clang_-wimplicit-const-int-float-conversion_in.patch
+fix_getcursorscreenpoint_wrongly_returns_0_0.patch
diff --git a/patches/chromium/fix_getcursorscreenpoint_wrongly_returns_0_0.patch b/patches/chromium/fix_getcursorscreenpoint_wrongly_returns_0_0.patch
new file mode 100644
index 0000000000000..d2e4bed589c78
--- /dev/null
+++ b/patches/chromium/fix_getcursorscreenpoint_wrongly_returns_0_0.patch
@@ -0,0 +1,25 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Charles Kerr <charles@charleskerr.com>
+Date: Thu, 8 Feb 2024 00:41:40 -0600
+Subject: fix: GetCursorScreenPoint() wrongly returns 0, 0
+
+Fixes #41143. Discussion of the issue at
+https://github.com/electron/electron/issues/41143#issuecomment-1933443163
+
+This patch should be backported to e29, upstreamed to Chromium, and then
+removed if it lands upstream.
+
+diff --git a/ui/events/x/events_x_utils.cc b/ui/events/x/events_x_utils.cc
+index aa047f2bd39643dc471b25eeb567b0dd3731e0e0..eb312fb1c115751041930539d0d5f15208461e04 100644
+--- a/ui/events/x/events_x_utils.cc
++++ b/ui/events/x/events_x_utils.cc
+@@ -586,6 +586,9 @@ gfx::Point EventLocationFromXEvent(const x11::Event& xev) {
+ gfx::Point EventSystemLocationFromXEvent(const x11::Event& xev) {
+   if (auto* crossing = xev.As<x11::CrossingEvent>())
+     return gfx::Point(crossing->root_x, crossing->root_y);
++  if (auto* crossing = xev.As<x11::Input::CrossingEvent>())
++    return gfx::Point(Fp1616ToDouble(crossing->root_x),
++                      Fp1616ToDouble(crossing->root_y));
+   if (auto* button = xev.As<x11::ButtonEvent>())
+     return gfx::Point(button->root_x, button->root_y);
+   if (auto* motion = xev.As<x11::MotionNotifyEvent>())


Test patch: