@@ -110,6 +110,7 @@ struct Decorations {
110110struct Layout {
111111 GdkRectangle frame = {}; // what the user sees as the window: title bar and content
112112 GdkRectangle content = {}; // the client area the application draws into
113+ Point content_origin_in_surface = {}; // independent of global Wayland coordinates
113114};
114115
115116// A window the window manager decorates is simple: its GdkWindow is the content and
@@ -168,6 +169,8 @@ static Layout GetLayout(GtkWidget* widget, GdkWindow* gdk_window) {
168169 }
169170 layout.frame = {origin_x + side, origin_y + shadow_top, width, height + title_height};
170171 layout.content = {origin_x + side, origin_y + shadow_top + title_height, width, height};
172+ layout.content_origin_in_surface = {static_cast <double >(side),
173+ static_cast <double >(shadow_top + title_height)};
171174#ifdef GDK_WINDOWING_WAYLAND
172175 if (GDK_IS_WAYLAND_DISPLAY (gdk_window_get_display (gdk_window))) {
173176 layout.content .x -= layout.frame .x ;
@@ -192,6 +195,7 @@ static Layout GetLayout(GtkWidget* widget, GdkWindow* gdk_window) {
192195
193196 layout.content = {origin_x + child_x, origin_y + child_y, child_allocation.width ,
194197 child_allocation.height };
198+ layout.content_origin_in_surface = {static_cast <double >(child_x), static_cast <double >(child_y)};
195199 // The frame is the content plus the title bar above it; the shadow is not part of it.
196200 gint frame_top = child_y;
197201 GtkWidget* titlebar = gtk_window_get_titlebar (GTK_WINDOW (widget));
@@ -466,7 +470,12 @@ class Window::Impl {
466470 : widget_(widget),
467471 gdk_window_ (gdk_window),
468472 title_bar_style_(TitleBarStyle::Normal),
469- background_color_(Color::White) {}
473+ background_color_(Color::White) {
474+ if (widget_)
475+ g_object_add_weak_pointer (G_OBJECT (widget_), reinterpret_cast <gpointer*>(&widget_));
476+ if (gdk_window_)
477+ g_object_add_weak_pointer (G_OBJECT (gdk_window_), reinterpret_cast <gpointer*>(&gdk_window_));
478+ }
470479
471480 ~Impl () {
472481 if (hints_refresh_source_) {
@@ -477,6 +486,10 @@ class Window::Impl {
477486 g_object_remove_weak_pointer (G_OBJECT (hints_widget_),
478487 reinterpret_cast <gpointer*>(&hints_widget_));
479488 }
489+ if (gdk_window_)
490+ g_object_remove_weak_pointer (G_OBJECT (gdk_window_), reinterpret_cast <gpointer*>(&gdk_window_));
491+ if (widget_)
492+ g_object_remove_weak_pointer (G_OBJECT (widget_), reinterpret_cast <gpointer*>(&widget_));
480493 }
481494
482495 void ApplyGeometryHints () {
@@ -1666,6 +1679,64 @@ bool Window::IsFocusable() const {
16661679 return AcceptsFocus (pimpl_->widget_ , pimpl_->gdk_window_ );
16671680}
16681681
1682+ bool Window::ShowSystemMenu (Point position) {
1683+ // GTK can replace its surface when decorations are installed. Read the live
1684+ // widget's surface instead of keeping a menu request on the previous one.
1685+ GdkWindow* surface = pimpl_->widget_ ? gtk_widget_get_window (pimpl_->widget_ ) : pimpl_->gdk_window_ ;
1686+ if (!surface || gdk_window_is_destroyed (surface) || !gdk_window_is_viewable (surface) ||
1687+ gdk_window_get_window_type (surface) != GDK_WINDOW_TOPLEVEL ||
1688+ !std::isfinite (position.x ) || !std::isfinite (position.y ) || !IsSystemMenuSupported ()) return false ;
1689+ GdkDisplay* display = gdk_window_get_display (surface);
1690+ GdkSeat* seat = gdk_display_get_default_seat (display);
1691+ GdkDevice* pointer = seat ? gdk_seat_get_pointer (seat) : nullptr ;
1692+ if (!pointer) return false ;
1693+ #ifdef GDK_WINDOWING_WAYLAND
1694+ if (GDK_IS_WAYLAND_DISPLAY (display)) {
1695+ GdkModifierType state = static_cast <GdkModifierType>(0 );
1696+ gdk_window_get_device_position (surface, pointer, nullptr , nullptr , &state);
1697+ GdkWindow* under_pointer = gdk_device_get_window_at_position (pointer, nullptr , nullptr );
1698+ if (!under_pointer || gdk_window_get_effective_toplevel (under_pointer) != surface ||
1699+ !(state & (GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK ))) return false ;
1700+ }
1701+ #endif
1702+ const Layout layout = GetLayout (pimpl_->widget_ , surface);
1703+ const double x = position.x + layout.content_origin_in_surface .x ;
1704+ const double y = position.y + layout.content_origin_in_surface .y ;
1705+ gint origin_x = 0 , origin_y = 0 ;
1706+ gdk_window_get_origin (surface, &origin_x, &origin_y);
1707+ const int scale = gdk_window_get_scale_factor (surface);
1708+ if (std::abs ((x + origin_x) * scale) > G_MAXINT ||
1709+ std::abs ((y + origin_y) * scale) > G_MAXINT ) return false ;
1710+ GdkEvent* event = gdk_event_new (GDK_BUTTON_PRESS );
1711+ event->button .window = GDK_WINDOW (g_object_ref (surface));
1712+ event->button .time = gtk_get_current_event_time ();
1713+ event->button .button = GDK_BUTTON_SECONDARY ;
1714+ event->button .x = x;
1715+ event->button .y = y;
1716+ event->button .x_root = x + origin_x;
1717+ event->button .y_root = y + origin_y;
1718+ gdk_event_set_device (event, pointer);
1719+ gdk_event_set_source_device (event, pointer);
1720+ const bool accepted = gdk_window_show_window_menu (surface, event);
1721+ gdk_event_free (event);
1722+ return accepted;
1723+ }
1724+
1725+ bool Window::IsSystemMenuSupported () {
1726+ GdkDisplay* display = gdk_display_get_default ();
1727+ if (!display) return false ;
1728+ #ifdef GDK_WINDOWING_X11
1729+ if (GDK_IS_X11_DISPLAY (display)) {
1730+ return gdk_x11_screen_supports_net_wm_hint (gdk_display_get_default_screen (display),
1731+ gdk_atom_intern_static_string (" _GTK_SHOW_WINDOW_MENU" ));
1732+ }
1733+ #endif
1734+ #ifdef GDK_WINDOWING_WAYLAND
1735+ if (GDK_IS_WAYLAND_DISPLAY (display)) return true ;
1736+ #endif
1737+ return false ;
1738+ }
1739+
16691740void Window::StartDragging () {
16701741 if (!pimpl_->gdk_window_ ) {
16711742 return ;
0 commit comments